halfcycle 0.4.5 → 0.4.7
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/.claude-plugin/plugin.json +1 -1
- package/bin/bin.bundle.mjs +1325 -155
- package/commands/agent-teams-orchestration.md +1 -1
- package/commands/stack-assembly-worker.md +1 -1
- package/commands/task-execution.md +1 -1
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -1
- package/dist/build-record/obtain.d.ts +3 -1
- package/dist/build-record/obtain.d.ts.map +1 -1
- package/dist/build-record/write.d.ts +5 -1
- package/dist/build-record/write.d.ts.map +1 -1
- package/dist/cli-contract.d.ts +1 -1
- package/dist/cli-contract.d.ts.map +1 -1
- package/dist/cli.js +2326 -190
- package/dist/cli.js.map +3 -3
- package/dist/close-phase.d.ts +8 -1
- package/dist/close-phase.d.ts.map +1 -1
- package/dist/draw/index.d.ts +135 -0
- package/dist/draw/index.d.ts.map +1 -0
- package/dist/draw/write.d.ts +282 -0
- package/dist/draw/write.d.ts.map +1 -0
- package/dist/index.js +96 -17
- package/dist/index.js.map +3 -3
- package/dist/install.d.ts +33 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/missing-route.d.ts +36 -0
- package/dist/missing-route.d.ts.map +1 -0
- package/dist/open-phase.d.ts +8 -1
- package/dist/open-phase.d.ts.map +1 -1
- package/dist/plan.d.ts +200 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/system-report.d.ts +179 -0
- package/dist/system-report.d.ts.map +1 -0
- package/package.json +1 -1
package/bin/bin.bundle.mjs
CHANGED
|
@@ -2266,8 +2266,8 @@ var Doc = class {
|
|
|
2266
2266
|
const lines = content.split("\n").filter((x) => x);
|
|
2267
2267
|
const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));
|
|
2268
2268
|
const dedented = lines.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
|
|
2269
|
-
for (const
|
|
2270
|
-
this.content.push(
|
|
2269
|
+
for (const line2 of dedented) {
|
|
2270
|
+
this.content.push(line2);
|
|
2271
2271
|
}
|
|
2272
2272
|
}
|
|
2273
2273
|
compile() {
|
|
@@ -14572,14 +14572,14 @@ function unquote(value) {
|
|
|
14572
14572
|
}
|
|
14573
14573
|
function parseEnvText(raw) {
|
|
14574
14574
|
const out = {};
|
|
14575
|
-
for (const
|
|
14576
|
-
const eq =
|
|
14575
|
+
for (const line2 of raw.split("\n")) {
|
|
14576
|
+
const eq = line2.indexOf("=");
|
|
14577
14577
|
if (eq === -1)
|
|
14578
14578
|
continue;
|
|
14579
|
-
const key =
|
|
14579
|
+
const key = line2.slice(0, eq).replace(/^\s*export\s+/, "").trim();
|
|
14580
14580
|
if (key === "" || key.startsWith("#"))
|
|
14581
14581
|
continue;
|
|
14582
|
-
out[key] = unquote(
|
|
14582
|
+
out[key] = unquote(line2.slice(eq + 1));
|
|
14583
14583
|
}
|
|
14584
14584
|
return out;
|
|
14585
14585
|
}
|
|
@@ -14588,13 +14588,13 @@ function reconcileEnvText(existing, values, keys) {
|
|
|
14588
14588
|
const value = values[k];
|
|
14589
14589
|
return [k, value === void 0 ? "" : value];
|
|
14590
14590
|
}));
|
|
14591
|
-
const
|
|
14591
|
+
const line2 = (k) => `${k}=${shq(desired.get(k) ?? "")}`;
|
|
14592
14592
|
const written = keys.filter((k) => desired.get(k) !== null);
|
|
14593
14593
|
if (existing === null) {
|
|
14594
14594
|
if (written.length === 0)
|
|
14595
14595
|
return "";
|
|
14596
14596
|
return `${ENGAGEMENT_ENV_HEADER}
|
|
14597
|
-
` + written.map(
|
|
14597
|
+
` + written.map(line2).join("\n") + "\n";
|
|
14598
14598
|
}
|
|
14599
14599
|
const seen = /* @__PURE__ */ new Set();
|
|
14600
14600
|
const out = [];
|
|
@@ -14615,13 +14615,13 @@ function reconcileEnvText(existing, values, keys) {
|
|
|
14615
14615
|
seen.add(key);
|
|
14616
14616
|
if (desired.get(key) === null)
|
|
14617
14617
|
continue;
|
|
14618
|
-
out.push(`${prefix}${
|
|
14618
|
+
out.push(`${prefix}${line2(key)}`);
|
|
14619
14619
|
}
|
|
14620
14620
|
const missing = written.filter((k) => !seen.has(k));
|
|
14621
14621
|
if (missing.length > 0) {
|
|
14622
14622
|
const header = existing.includes(ENGAGEMENT_ENV_HEADER) ? "" : `${ENGAGEMENT_ENV_HEADER}
|
|
14623
14623
|
`;
|
|
14624
|
-
const block = header + missing.map(
|
|
14624
|
+
const block = header + missing.map(line2).join("\n");
|
|
14625
14625
|
const trailingBlank = out.length > 0 && out[out.length - 1] === "";
|
|
14626
14626
|
if (trailingBlank)
|
|
14627
14627
|
out.splice(out.length - 1, 0, block);
|
|
@@ -14662,6 +14662,7 @@ var VENDORED_BIN_REL = ".halfcycle/bin/bin.bundle.mjs";
|
|
|
14662
14662
|
var SESSION_START_MARKER_REL = ".claude/hooks/session-start-marker.sh";
|
|
14663
14663
|
var GUARD_RUNNER_REL = ".claude/hooks/guard-runner.sh";
|
|
14664
14664
|
var USER_PROMPT_REMINDER_REL = ".claude/hooks/user-prompt-reminder.sh";
|
|
14665
|
+
var USER_PROMPT_SCOPE_REL = ".claude/hooks/user-prompt-scope.sh";
|
|
14665
14666
|
var SESSION_END_MARKER_REL = ".claude/hooks/session-end-marker.sh";
|
|
14666
14667
|
var WRITE_CAPABLE_TOOLS = ["Edit", "MultiEdit", "NotebookEdit", "Write"];
|
|
14667
14668
|
function hookCommand(script, verb) {
|
|
@@ -14690,6 +14691,13 @@ var HALFCYCLE_HOOK_WIRING = [
|
|
|
14690
14691
|
command: hookCommand(USER_PROMPT_REMINDER_REL),
|
|
14691
14692
|
script: USER_PROMPT_REMINDER_REL
|
|
14692
14693
|
},
|
|
14694
|
+
{
|
|
14695
|
+
event: "UserPromptSubmit",
|
|
14696
|
+
matcher: null,
|
|
14697
|
+
timeout: 10,
|
|
14698
|
+
command: hookCommand(USER_PROMPT_SCOPE_REL),
|
|
14699
|
+
script: USER_PROMPT_SCOPE_REL
|
|
14700
|
+
},
|
|
14693
14701
|
{
|
|
14694
14702
|
event: "PostToolUse",
|
|
14695
14703
|
matcher: WRITE_CAPABLE_TOOLS.join("|"),
|
|
@@ -14765,6 +14773,98 @@ function looksLikeSecret(text) {
|
|
|
14765
14773
|
var MCP_REGISTRATION_REL = ".mcp.json";
|
|
14766
14774
|
var MCP_SERVER_KEY = "halfcycle";
|
|
14767
14775
|
|
|
14776
|
+
// ../core/dist/shell-quote.js
|
|
14777
|
+
function quoteForShell(value) {
|
|
14778
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
14779
|
+
}
|
|
14780
|
+
var HALFCYCLE_COMMAND = "npx halfcycle@latest";
|
|
14781
|
+
|
|
14782
|
+
// ../core/dist/shown-plans.js
|
|
14783
|
+
var SHOWN_PLANS_FILENAME = "shown-plans.json";
|
|
14784
|
+
var SHOWN_PLANS_FORMAT = 1;
|
|
14785
|
+
var RECORD_KEYS = ["entries", "format"];
|
|
14786
|
+
var ENTRY_KEYS = ["number", "phase", "shownAt", "version"];
|
|
14787
|
+
function parseShownPlans(text) {
|
|
14788
|
+
try {
|
|
14789
|
+
if (typeof text !== "string")
|
|
14790
|
+
return [];
|
|
14791
|
+
const parsed = JSON.parse(text);
|
|
14792
|
+
if (!isPlainObject2(parsed) || !hasExactKeys(parsed, RECORD_KEYS))
|
|
14793
|
+
return [];
|
|
14794
|
+
if (parsed["format"] !== SHOWN_PLANS_FORMAT)
|
|
14795
|
+
return [];
|
|
14796
|
+
const rawEntries = parsed["entries"];
|
|
14797
|
+
if (!Array.isArray(rawEntries))
|
|
14798
|
+
return [];
|
|
14799
|
+
const entries = [];
|
|
14800
|
+
for (const raw of rawEntries) {
|
|
14801
|
+
const entry = readEntry(raw);
|
|
14802
|
+
if (entry === null)
|
|
14803
|
+
return [];
|
|
14804
|
+
entries.push(entry);
|
|
14805
|
+
}
|
|
14806
|
+
return hasDuplicates(entries) ? [] : entries;
|
|
14807
|
+
} catch {
|
|
14808
|
+
return [];
|
|
14809
|
+
}
|
|
14810
|
+
}
|
|
14811
|
+
function readEntry(raw) {
|
|
14812
|
+
if (!isPlainObject2(raw) || !hasExactKeys(raw, ENTRY_KEYS))
|
|
14813
|
+
return null;
|
|
14814
|
+
const phase = raw["phase"];
|
|
14815
|
+
const number4 = raw["number"];
|
|
14816
|
+
const version2 = raw["version"];
|
|
14817
|
+
const shownAt = raw["shownAt"];
|
|
14818
|
+
if (typeof phase !== "string" || phase === "")
|
|
14819
|
+
return null;
|
|
14820
|
+
if (!isPositiveInteger(number4) || !isPositiveInteger(version2))
|
|
14821
|
+
return null;
|
|
14822
|
+
if (!isIsoInstant(shownAt))
|
|
14823
|
+
return null;
|
|
14824
|
+
return { phase, number: number4, version: version2, shownAt };
|
|
14825
|
+
}
|
|
14826
|
+
function isPlainObject2(value) {
|
|
14827
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
14828
|
+
return false;
|
|
14829
|
+
const proto = Object.getPrototypeOf(value);
|
|
14830
|
+
return proto === Object.prototype || proto === null;
|
|
14831
|
+
}
|
|
14832
|
+
function hasExactKeys(value, keys) {
|
|
14833
|
+
const own = Object.keys(value).sort();
|
|
14834
|
+
return own.length === keys.length && own.every((key, i) => key === keys[i]);
|
|
14835
|
+
}
|
|
14836
|
+
function isPositiveInteger(value) {
|
|
14837
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
14838
|
+
}
|
|
14839
|
+
function isIsoInstant(value) {
|
|
14840
|
+
if (typeof value !== "string")
|
|
14841
|
+
return false;
|
|
14842
|
+
const ms = Date.parse(value);
|
|
14843
|
+
return Number.isFinite(ms) && new Date(ms).toISOString() === value;
|
|
14844
|
+
}
|
|
14845
|
+
function hasDuplicates(entries) {
|
|
14846
|
+
const phases = new Set(entries.map((entry) => entry.phase));
|
|
14847
|
+
const numbers = new Set(entries.map((entry) => entry.number));
|
|
14848
|
+
return phases.size !== entries.length || numbers.size !== entries.length;
|
|
14849
|
+
}
|
|
14850
|
+
|
|
14851
|
+
// ../core/dist/phase-reference.js
|
|
14852
|
+
var TYPED_NUMBER = /^(?:0|[1-9][0-9]*)$/;
|
|
14853
|
+
function resolvePhaseReference(typed, phases) {
|
|
14854
|
+
if (typeof typed !== "string" || typed === "")
|
|
14855
|
+
return null;
|
|
14856
|
+
const byIdentity = phases.filter((entry) => entry.phase === typed);
|
|
14857
|
+
if (byIdentity.length > 0)
|
|
14858
|
+
return byIdentity.length === 1 ? byIdentity[0] : null;
|
|
14859
|
+
if (!TYPED_NUMBER.test(typed))
|
|
14860
|
+
return null;
|
|
14861
|
+
const byNumber = phases.filter((entry) => Number.isInteger(entry.number) && String(entry.number) === typed);
|
|
14862
|
+
return byNumber.length === 1 ? byNumber[0] : null;
|
|
14863
|
+
}
|
|
14864
|
+
|
|
14865
|
+
// ../core/dist/release-version.js
|
|
14866
|
+
var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
14867
|
+
|
|
14768
14868
|
// dist/control-origin.js
|
|
14769
14869
|
var DEFAULT_CONTROL_ORIGIN = "https://control.halfcycle.ai";
|
|
14770
14870
|
function resolveControlOrigin(env) {
|
|
@@ -14830,6 +14930,9 @@ function readMcpServerUrl() {
|
|
|
14830
14930
|
const raw = process.env["CLAUDE_CODE_MCP_SERVER_URL"];
|
|
14831
14931
|
return raw ? raw : void 0;
|
|
14832
14932
|
}
|
|
14933
|
+
function readSessionAttended(env = process.env) {
|
|
14934
|
+
return env["CLAUDE_CODE_SESSION_ATTENDED"] === "1";
|
|
14935
|
+
}
|
|
14833
14936
|
function readExcerptConfig() {
|
|
14834
14937
|
const raw = process.env["HALFCYCLE_SOURCE_EXCERPTS"];
|
|
14835
14938
|
const enabled = (raw ?? "").trim().toLowerCase() !== "false";
|
|
@@ -15040,14 +15143,25 @@ var wireErrorSchema = external_exports.object({
|
|
|
15040
15143
|
message: external_exports.string()
|
|
15041
15144
|
}).strict();
|
|
15042
15145
|
|
|
15146
|
+
// ../events/dist/lone-surrogates.js
|
|
15147
|
+
var LONE_SURROGATE = /\p{Cs}/u;
|
|
15148
|
+
function hasLoneSurrogate(text) {
|
|
15149
|
+
return LONE_SURROGATE.test(text);
|
|
15150
|
+
}
|
|
15151
|
+
var LONE_SURROGATE_MESSAGE = "must be valid text: it holds half of a character pair with no other half";
|
|
15152
|
+
|
|
15043
15153
|
// ../events/dist/state.js
|
|
15044
15154
|
var utcIso8601 = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
|
|
15045
15155
|
var STATE_RECORD_FORMAT = "halfcycle-state-record/v1";
|
|
15046
15156
|
var stepIdSchema = external_exports.string().regex(/^(l[0-5](-l[0-5])?|xl)\.[a-z0-9-]+$/);
|
|
15047
|
-
var methodVersionSchema = external_exports.string().regex(
|
|
15157
|
+
var methodVersionSchema = external_exports.string().regex(RELEASE_VERSION_PATTERN);
|
|
15048
15158
|
var anchorPathRegex = /^(?!\/)(?!.*#)(?!method\/)(?!(.*\/)?docs\/method\/).+$/;
|
|
15049
15159
|
var recordKindSchema = external_exports.enum(["artefact", "step-run", "gate", "intervention"]);
|
|
15050
15160
|
var methodLayerSchema = external_exports.enum(["l0", "l1", "l2", "l3", "l3-l4", "l4", "l5", "xl"]);
|
|
15161
|
+
var PHASE_SCOPED_LAYERS = ["l4", "l5"];
|
|
15162
|
+
function isPhaseScopedLayer(layer) {
|
|
15163
|
+
return PHASE_SCOPED_LAYERS.includes(layer);
|
|
15164
|
+
}
|
|
15051
15165
|
var evidenceKindSchema = external_exports.enum(["anchor", "diff", "repro", "question-answer", "no-op"]);
|
|
15052
15166
|
var declarableEvidenceKindSchema = external_exports.enum(["anchor", "diff", "repro", "question-answer"]);
|
|
15053
15167
|
var dispositionSchema = external_exports.enum([
|
|
@@ -15295,6 +15409,683 @@ function describeIdentity(identity) {
|
|
|
15295
15409
|
return `(a ${typeof identity})`;
|
|
15296
15410
|
}
|
|
15297
15411
|
|
|
15412
|
+
// ../events/dist/build-record.js
|
|
15413
|
+
var BUILD_RECORD_V2_FORMAT = "halfcycle-build-record/v2";
|
|
15414
|
+
var STATEMENT_MAX = 2e3;
|
|
15415
|
+
var DEVIATION_MAX = 500;
|
|
15416
|
+
var DEVIATIONS_MAX = 20;
|
|
15417
|
+
var ACTOR_MAX = 200;
|
|
15418
|
+
var FINDING_MAX = 500;
|
|
15419
|
+
var FINDINGS_SHOWN_MAX = 50;
|
|
15420
|
+
var PHASE_CLOSE_REASON_MAX = 1900;
|
|
15421
|
+
var closeCommitSchema = external_exports.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full commit hash: 40 or 64 lowercase hex characters");
|
|
15422
|
+
var walkVerdictSchema = external_exports.enum(["clean", "defects"]);
|
|
15423
|
+
var closeDecisionSchema = external_exports.enum(["done-when-met", "override"]);
|
|
15424
|
+
function plainText(max) {
|
|
15425
|
+
const base = max === void 0 ? external_exports.string() : external_exports.string().max(max);
|
|
15426
|
+
return base.refine((v) => !hasControlCharacter(v), {
|
|
15427
|
+
message: "must be one line with no control characters (no line breaks, tabs or NUL)"
|
|
15428
|
+
}).refine((v) => !hasLoneSurrogate(v), { message: LONE_SURROGATE_MESSAGE });
|
|
15429
|
+
}
|
|
15430
|
+
function statementText(max) {
|
|
15431
|
+
return plainText(max).refine((v) => !looksLikeSecret(v), {
|
|
15432
|
+
message: "looks like a password or key \u2014 rephrase it"
|
|
15433
|
+
});
|
|
15434
|
+
}
|
|
15435
|
+
var closeActorSchema = plainText(ACTOR_MAX);
|
|
15436
|
+
var PHASE_NAME_RULE = `must be a phase name: not empty, no slash or backslash, not starting with a dot, no control characters, at most ${PHASE_NAME_MAX_BYTES} bytes`;
|
|
15437
|
+
var phaseNameSchema = external_exports.string().refine((v) => isValidPhaseIdentity(v), {
|
|
15438
|
+
message: PHASE_NAME_RULE
|
|
15439
|
+
});
|
|
15440
|
+
var phaseIdentitySchema = external_exports.union([external_exports.string(), external_exports.number()], { error: "must be a phase name or a whole number" }).refine((v) => isValidPhaseIdentity(v), { message: PHASE_NAME_RULE });
|
|
15441
|
+
var closeStatementsSchema = external_exports.object({
|
|
15442
|
+
thesis: statementText(STATEMENT_MAX).nullable(),
|
|
15443
|
+
outcome: statementText(STATEMENT_MAX).nullable(),
|
|
15444
|
+
deviations: external_exports.array(statementText(DEVIATION_MAX)).max(DEVIATIONS_MAX),
|
|
15445
|
+
closeCommit: closeCommitSchema.nullable()
|
|
15446
|
+
}).strict();
|
|
15447
|
+
var closedBySchema = external_exports.discriminatedUnion("kind", [
|
|
15448
|
+
external_exports.object({ kind: external_exports.literal("account"), email: plainText().nullable() }).strict(),
|
|
15449
|
+
external_exports.object({ kind: external_exports.literal("no-account") }).strict(),
|
|
15450
|
+
external_exports.object({ kind: external_exports.literal("account-removed") }).strict(),
|
|
15451
|
+
external_exports.object({ kind: external_exports.literal("not-recorded") }).strict()
|
|
15452
|
+
]);
|
|
15453
|
+
var STEP_NAME_MAX = 200;
|
|
15454
|
+
var stepNameSchema = plainText(STEP_NAME_MAX).refine((v) => v.trim().length > 0, {
|
|
15455
|
+
message: "must not be blank"
|
|
15456
|
+
});
|
|
15457
|
+
var unfinishedStepSchema = external_exports.object({
|
|
15458
|
+
stepId: plainText(),
|
|
15459
|
+
state: external_exports.enum(["parked", "failed"]),
|
|
15460
|
+
name: stepNameSchema.optional()
|
|
15461
|
+
}).strict();
|
|
15462
|
+
var crossedSchema = external_exports.discriminatedUnion("kind", [
|
|
15463
|
+
external_exports.object({ kind: external_exports.literal("none") }).strict(),
|
|
15464
|
+
external_exports.object({ kind: external_exports.literal("unfinished-steps"), steps: external_exports.array(unfinishedStepSchema).min(1) }).strict(),
|
|
15465
|
+
external_exports.object({ kind: external_exports.literal("clean-over-findings"), findings: external_exports.number().int().min(1) }).strict(),
|
|
15466
|
+
external_exports.object({ kind: external_exports.literal("not-recorded") }).strict()
|
|
15467
|
+
]);
|
|
15468
|
+
var seatCoverageSchema = external_exports.object({
|
|
15469
|
+
enrolled: external_exports.number().int().nonnegative(),
|
|
15470
|
+
conformant: external_exports.number().int().nonnegative()
|
|
15471
|
+
}).strict().refine((c) => c.conformant <= c.enrolled, {
|
|
15472
|
+
message: "conformant cannot exceed enrolled",
|
|
15473
|
+
path: ["conformant"]
|
|
15474
|
+
});
|
|
15475
|
+
var observedCloseSchema = external_exports.object({
|
|
15476
|
+
sequence: external_exports.number().int().min(1),
|
|
15477
|
+
closedAt: utcIso8601,
|
|
15478
|
+
closedBy: closedBySchema,
|
|
15479
|
+
crossed: crossedSchema,
|
|
15480
|
+
seatCoverage: seatCoverageSchema.nullable()
|
|
15481
|
+
}).strict();
|
|
15482
|
+
var couldNotRunCheckSchema = external_exports.object({
|
|
15483
|
+
check: plainText(),
|
|
15484
|
+
description: plainText().nullable(),
|
|
15485
|
+
reason: plainText()
|
|
15486
|
+
}).strict();
|
|
15487
|
+
var firedCheckSchema = external_exports.object({
|
|
15488
|
+
check: plainText(),
|
|
15489
|
+
description: plainText().nullable(),
|
|
15490
|
+
severity: severitySchema,
|
|
15491
|
+
explanation: plainText().nullable(),
|
|
15492
|
+
occurrences: external_exports.number().int().min(1)
|
|
15493
|
+
}).strict();
|
|
15494
|
+
var phaseChecksSchema = external_exports.discriminatedUnion("state", [
|
|
15495
|
+
external_exports.object({ state: external_exports.literal("not-observed") }).strict(),
|
|
15496
|
+
external_exports.object({
|
|
15497
|
+
state: external_exports.literal("nothing-fired"),
|
|
15498
|
+
runs: external_exports.number().int().min(1),
|
|
15499
|
+
couldNotRun: external_exports.array(couldNotRunCheckSchema)
|
|
15500
|
+
}).strict(),
|
|
15501
|
+
external_exports.object({
|
|
15502
|
+
state: external_exports.literal("fired"),
|
|
15503
|
+
runs: external_exports.number().int().min(1),
|
|
15504
|
+
couldNotRun: external_exports.array(couldNotRunCheckSchema),
|
|
15505
|
+
fired: external_exports.array(firedCheckSchema).min(1)
|
|
15506
|
+
}).strict()
|
|
15507
|
+
]);
|
|
15508
|
+
var observedSchema = external_exports.object({
|
|
15509
|
+
engagementId: plainText(),
|
|
15510
|
+
methodVersion: methodVersionSchema,
|
|
15511
|
+
closes: external_exports.array(observedCloseSchema).min(1),
|
|
15512
|
+
checks: phaseChecksSchema
|
|
15513
|
+
}).strict().superRefine((o, ctx) => {
|
|
15514
|
+
o.closes.forEach((close, i) => {
|
|
15515
|
+
if (close.sequence !== i + 1) {
|
|
15516
|
+
ctx.addIssue({
|
|
15517
|
+
code: "custom",
|
|
15518
|
+
message: "closes must be numbered 1, 2, 3 \u2026 in order with no gap",
|
|
15519
|
+
path: ["closes", i, "sequence"]
|
|
15520
|
+
});
|
|
15521
|
+
}
|
|
15522
|
+
});
|
|
15523
|
+
});
|
|
15524
|
+
var attestedWalkSchema = external_exports.object({
|
|
15525
|
+
verdict: walkVerdictSchema,
|
|
15526
|
+
findings: external_exports.array(plainText(FINDING_MAX)).max(FINDINGS_SHOWN_MAX),
|
|
15527
|
+
findingsNotShown: external_exports.number().int().nonnegative()
|
|
15528
|
+
}).strict();
|
|
15529
|
+
var attestedStatementsSchema = external_exports.object({
|
|
15530
|
+
thesis: plainText(STATEMENT_MAX).nullable(),
|
|
15531
|
+
outcome: plainText(STATEMENT_MAX).nullable(),
|
|
15532
|
+
deviations: external_exports.array(plainText(DEVIATION_MAX)).max(DEVIATIONS_MAX)
|
|
15533
|
+
}).strict();
|
|
15534
|
+
var attestedCloseSchema = external_exports.object({
|
|
15535
|
+
sequence: external_exports.number().int().min(1),
|
|
15536
|
+
decision: closeDecisionSchema,
|
|
15537
|
+
actor: plainText(ACTOR_MAX).nullable(),
|
|
15538
|
+
reason: plainText(PHASE_CLOSE_REASON_MAX).nullable(),
|
|
15539
|
+
walk: attestedWalkSchema.nullable(),
|
|
15540
|
+
closeCommit: closeCommitSchema.nullable(),
|
|
15541
|
+
statements: attestedStatementsSchema
|
|
15542
|
+
}).strict();
|
|
15543
|
+
var attestedSchema = external_exports.object({
|
|
15544
|
+
closes: external_exports.array(attestedCloseSchema)
|
|
15545
|
+
}).strict();
|
|
15546
|
+
var buildRecordV2Schema = external_exports.object({
|
|
15547
|
+
format: external_exports.literal(BUILD_RECORD_V2_FORMAT),
|
|
15548
|
+
phase: phaseNameSchema,
|
|
15549
|
+
observed: observedSchema,
|
|
15550
|
+
attested: attestedSchema
|
|
15551
|
+
}).strict().superRefine((record2, ctx) => {
|
|
15552
|
+
const observedBySequence = new Map(record2.observed.closes.map((c) => [c.sequence, c]));
|
|
15553
|
+
const attestedSequences = record2.attested.closes.map((c) => c.sequence);
|
|
15554
|
+
const sameSet = attestedSequences.length === observedBySequence.size && new Set(attestedSequences).size === attestedSequences.length && attestedSequences.every((s) => observedBySequence.has(s));
|
|
15555
|
+
if (!sameSet) {
|
|
15556
|
+
ctx.addIssue({
|
|
15557
|
+
code: "custom",
|
|
15558
|
+
message: "attested closes must match the observed closes one for one, by sequence",
|
|
15559
|
+
path: ["attested", "closes"]
|
|
15560
|
+
});
|
|
15561
|
+
}
|
|
15562
|
+
record2.attested.closes.forEach((close, i) => {
|
|
15563
|
+
if (close.decision === "override" !== (close.reason !== null)) {
|
|
15564
|
+
ctx.addIssue({
|
|
15565
|
+
code: "custom",
|
|
15566
|
+
message: "reason is given exactly when the decision is override",
|
|
15567
|
+
path: ["attested", "closes", i, "reason"]
|
|
15568
|
+
});
|
|
15569
|
+
}
|
|
15570
|
+
const observed = observedBySequence.get(close.sequence);
|
|
15571
|
+
if (observed !== void 0 && observed.closedBy.kind === "account-removed" !== (close.actor === null)) {
|
|
15572
|
+
ctx.addIssue({
|
|
15573
|
+
code: "custom",
|
|
15574
|
+
message: "actor is empty exactly when the closing account was removed",
|
|
15575
|
+
path: ["attested", "closes", i, "actor"]
|
|
15576
|
+
});
|
|
15577
|
+
}
|
|
15578
|
+
});
|
|
15579
|
+
});
|
|
15580
|
+
|
|
15581
|
+
// ../events/dist/human-agent.js
|
|
15582
|
+
var HUMAN_AGENT = "Human";
|
|
15583
|
+
|
|
15584
|
+
// ../events/dist/build-snapshot.js
|
|
15585
|
+
var buildTaskStateSchema = external_exports.enum([
|
|
15586
|
+
"pending",
|
|
15587
|
+
"in-flight",
|
|
15588
|
+
"completed",
|
|
15589
|
+
"failed",
|
|
15590
|
+
"blocked"
|
|
15591
|
+
]);
|
|
15592
|
+
var BUILD_TASK_TITLE_MAX = 240;
|
|
15593
|
+
var BUILD_SNAPSHOT_TASK_LIMIT = 500;
|
|
15594
|
+
var buildTaskSchema = external_exports.object({
|
|
15595
|
+
// The task's own id, as the coordinator's graph names it.
|
|
15596
|
+
id: external_exports.string().min(1).max(64),
|
|
15597
|
+
// What the task is, in the coordinator's words. A description of the work,
|
|
15598
|
+
// bounded and scanned — never an excerpt of anybody's code.
|
|
15599
|
+
title: external_exports.string().min(1).max(BUILD_TASK_TITLE_MAX),
|
|
15600
|
+
// What the task is doing right now.
|
|
15601
|
+
state: buildTaskStateSchema,
|
|
15602
|
+
// Who holds it — a bare callsign, or the value naming a person when the work is
|
|
15603
|
+
// a person's. Absent when nothing has been dispatched to it yet.
|
|
15604
|
+
agent: external_exports.string().min(1).max(32).optional(),
|
|
15605
|
+
// The kind of work the holder does. Present exactly when an agent holds it.
|
|
15606
|
+
discipline: external_exports.string().min(1).max(64).optional(),
|
|
15607
|
+
// How much thinking the holder brings. Present exactly when an agent holds it,
|
|
15608
|
+
// and there is no field naming the supplier behind it.
|
|
15609
|
+
tier: external_exports.string().min(1).max(32).optional(),
|
|
15610
|
+
// The tasks this one cannot start before. Always present; empty means it waits
|
|
15611
|
+
// on nothing, which is different from not having been computed.
|
|
15612
|
+
dependsOn: external_exports.array(external_exports.string().min(1).max(64)).max(BUILD_SNAPSHOT_TASK_LIMIT),
|
|
15613
|
+
// The one task this task checks, when it is a check on other work. Absent on a
|
|
15614
|
+
// task that is doing the work rather than checking it.
|
|
15615
|
+
reviews: external_exports.string().min(1).max(64).optional(),
|
|
15616
|
+
// Which wave of the phase this task belongs to — the wave that writes the
|
|
15617
|
+
// specifications, or the wave that builds them. Absent when the graph does not
|
|
15618
|
+
// record it, which is different from the task belonging to neither.
|
|
15619
|
+
layer: methodLayerSchema.optional(),
|
|
15620
|
+
// When it was picked up, and when it settled. A reader computes how long it
|
|
15621
|
+
// took; neither this row nor this payload carries a duration.
|
|
15622
|
+
startedAt: utcIso8601.optional(),
|
|
15623
|
+
settledAt: utcIso8601.optional()
|
|
15624
|
+
}).strict().superRefine((task, ctx) => {
|
|
15625
|
+
if (task.reviews !== void 0 && task.reviews === task.id) {
|
|
15626
|
+
ctx.addIssue({
|
|
15627
|
+
code: external_exports.ZodIssueCode.custom,
|
|
15628
|
+
path: ["reviews"],
|
|
15629
|
+
message: `'${task.id}' reviews itself \u2014 a review names the OTHER task it checks.`
|
|
15630
|
+
});
|
|
15631
|
+
}
|
|
15632
|
+
if (task.layer !== void 0 && !isPhaseScopedLayer(task.layer)) {
|
|
15633
|
+
ctx.addIssue({
|
|
15634
|
+
code: external_exports.ZodIssueCode.custom,
|
|
15635
|
+
path: ["layer"],
|
|
15636
|
+
message: `'${task.layer}' is not a wave a phase runs \u2014 expected one of ${PHASE_SCOPED_LAYERS.join(", ")}.`
|
|
15637
|
+
});
|
|
15638
|
+
}
|
|
15639
|
+
const profiled = task.agent !== void 0 && task.agent !== HUMAN_AGENT;
|
|
15640
|
+
for (const field of ["discipline", "tier"]) {
|
|
15641
|
+
if (profiled && task[field] === void 0) {
|
|
15642
|
+
ctx.addIssue({
|
|
15643
|
+
code: external_exports.ZodIssueCode.custom,
|
|
15644
|
+
path: [field],
|
|
15645
|
+
message: `'${field}' is required on a task an agent holds.`
|
|
15646
|
+
});
|
|
15647
|
+
}
|
|
15648
|
+
if (!profiled && task[field] !== void 0) {
|
|
15649
|
+
ctx.addIssue({
|
|
15650
|
+
code: external_exports.ZodIssueCode.custom,
|
|
15651
|
+
path: [field],
|
|
15652
|
+
message: task.agent === void 0 ? `'${field}' must be absent on a task nobody holds.` : `'${field}' must be absent on work a person holds.`
|
|
15653
|
+
});
|
|
15654
|
+
}
|
|
15655
|
+
}
|
|
15656
|
+
});
|
|
15657
|
+
var buildPhaseSchema = external_exports.string().min(1).max(128);
|
|
15658
|
+
var BUILD_PHASE_TITLE_MAX = 160;
|
|
15659
|
+
var buildPhaseTitleSchema = external_exports.string().min(1).max(BUILD_PHASE_TITLE_MAX);
|
|
15660
|
+
var buildTaskListSchema = external_exports.array(buildTaskSchema).max(BUILD_SNAPSHOT_TASK_LIMIT).superRefine((tasks, ctx) => {
|
|
15661
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15662
|
+
for (const [index, task] of tasks.entries()) {
|
|
15663
|
+
if (seen.has(task.id)) {
|
|
15664
|
+
ctx.addIssue({
|
|
15665
|
+
code: external_exports.ZodIssueCode.custom,
|
|
15666
|
+
path: [index, "id"],
|
|
15667
|
+
message: `duplicate task id '${task.id}' \u2014 one graph names each task once.`
|
|
15668
|
+
});
|
|
15669
|
+
}
|
|
15670
|
+
seen.add(task.id);
|
|
15671
|
+
}
|
|
15672
|
+
});
|
|
15673
|
+
var buildSnapshotSchema = external_exports.object({
|
|
15674
|
+
// Which phase this graph belongs to.
|
|
15675
|
+
phase: buildPhaseSchema,
|
|
15676
|
+
// That phase's name in words, when the project gives it one.
|
|
15677
|
+
phaseTitle: buildPhaseTitleSchema.optional(),
|
|
15678
|
+
// Every task on it. Never a partial graph and never a change since last time.
|
|
15679
|
+
tasks: buildTaskListSchema,
|
|
15680
|
+
// When this graph was last reported.
|
|
15681
|
+
reportedAt: utcIso8601,
|
|
15682
|
+
// When work on this phase was first reported with no approval of its scope in force.
|
|
15683
|
+
builtWithoutApproval: utcIso8601.optional()
|
|
15684
|
+
}).strict();
|
|
15685
|
+
var buildPhaseEntrySchema = external_exports.object({
|
|
15686
|
+
// Which phase has a stored graph.
|
|
15687
|
+
phase: buildPhaseSchema,
|
|
15688
|
+
// That phase's name in words, when the project gave it one.
|
|
15689
|
+
phaseTitle: buildPhaseTitleSchema.optional(),
|
|
15690
|
+
// When that phase's graph was last reported.
|
|
15691
|
+
reportedAt: utcIso8601,
|
|
15692
|
+
// Present when work on this phase was reported with no approval of its scope in force.
|
|
15693
|
+
builtWithoutApproval: external_exports.literal(true).optional()
|
|
15694
|
+
}).strict();
|
|
15695
|
+
var buildSnapshotReportSchema = external_exports.object({
|
|
15696
|
+
// Which phase this graph belongs to.
|
|
15697
|
+
phase: buildPhaseSchema,
|
|
15698
|
+
// That phase's name in words, when the project gives it one.
|
|
15699
|
+
phaseTitle: buildPhaseTitleSchema.optional(),
|
|
15700
|
+
// The whole graph, every time. Reporting a change instead of the graph is the
|
|
15701
|
+
// one thing this route does not accept.
|
|
15702
|
+
tasks: buildTaskListSchema
|
|
15703
|
+
}).strict();
|
|
15704
|
+
var buildSnapshotReportResultSchema = external_exports.object({
|
|
15705
|
+
// What happened. One outcome today; a refusal travels as the shared wire error.
|
|
15706
|
+
outcome: external_exports.literal("recorded"),
|
|
15707
|
+
// Which phase the graph was recorded against.
|
|
15708
|
+
phase: buildPhaseSchema,
|
|
15709
|
+
// When it was recorded.
|
|
15710
|
+
reportedAt: utcIso8601
|
|
15711
|
+
}).strict();
|
|
15712
|
+
|
|
15713
|
+
// ../events/dist/system-description.js
|
|
15714
|
+
var SYSTEM_DESCRIPTION_FORMAT = 1;
|
|
15715
|
+
var SYSTEM_PARTS_MAX = 60;
|
|
15716
|
+
var SYSTEM_CONNECTIONS_MAX = 400;
|
|
15717
|
+
var SYSTEM_SLOT_MAX = 11;
|
|
15718
|
+
var SYSTEM_OWNS_MAX = 20;
|
|
15719
|
+
var SYSTEM_OWNED_PATTERN_MAX = 200;
|
|
15720
|
+
var SYSTEM_PART_NAME_MAX = 60;
|
|
15721
|
+
var SYSTEM_PART_DESCRIPTION_MAX = 160;
|
|
15722
|
+
var SYSTEM_CONNECTION_LABEL_MAX = 60;
|
|
15723
|
+
var SYSTEM_PART_ID_PATTERN = /^[a-z][a-z0-9-]{0,39}$/;
|
|
15724
|
+
var SYSTEM_LANES = ["clients", "edge", "services", "data", "external"];
|
|
15725
|
+
var systemLaneSchema = external_exports.enum(SYSTEM_LANES, {
|
|
15726
|
+
error: `must be one of ${SYSTEM_LANES.join(", ")}`
|
|
15727
|
+
});
|
|
15728
|
+
var SYSTEM_CONNECTION_KINDS = ["call", "event", "write", "external"];
|
|
15729
|
+
var systemConnectionKindSchema = external_exports.enum(SYSTEM_CONNECTION_KINDS, {
|
|
15730
|
+
error: `must be one of ${SYSTEM_CONNECTION_KINDS.join(", ")}`
|
|
15731
|
+
});
|
|
15732
|
+
var LINE_SEPARATOR = /[\u2028\u2029]/;
|
|
15733
|
+
function descriptionText(max) {
|
|
15734
|
+
return statementText(max).refine((v) => v.trim().length > 0, { message: "must not be empty or only spaces" }).refine((v) => !LINE_SEPARATOR.test(v), {
|
|
15735
|
+
message: "must be one line: it holds a line or paragraph separator"
|
|
15736
|
+
});
|
|
15737
|
+
}
|
|
15738
|
+
var systemPartIdSchema = external_exports.string().regex(SYSTEM_PART_ID_PATTERN, {
|
|
15739
|
+
message: "must be a short id: a lowercase letter, then lowercase letters, digits or hyphens, 40 characters at most"
|
|
15740
|
+
}).refine((v) => !looksLikeSecret(v), { message: "looks like a password or key; choose another id" });
|
|
15741
|
+
function hasEmptySegment(pattern) {
|
|
15742
|
+
return pattern.split("/").some((segment) => segment.length === 0);
|
|
15743
|
+
}
|
|
15744
|
+
function hasDotSegment(pattern) {
|
|
15745
|
+
return pattern.split("/").some((segment) => segment === "." || segment === "..");
|
|
15746
|
+
}
|
|
15747
|
+
function hasPartialDoubleStar(pattern) {
|
|
15748
|
+
return pattern.split("/").some((segment) => segment.includes("**") && segment !== "**");
|
|
15749
|
+
}
|
|
15750
|
+
var SEGMENT_CHARACTERS = /^[A-Za-z0-9._\-*]*$/;
|
|
15751
|
+
function hasForeignCharacter(pattern) {
|
|
15752
|
+
return pattern.split("/").some((segment) => !SEGMENT_CHARACTERS.test(segment));
|
|
15753
|
+
}
|
|
15754
|
+
var systemOwnedPatternSchema = external_exports.string().max(SYSTEM_OWNED_PATTERN_MAX, { message: `must be at most ${SYSTEM_OWNED_PATTERN_MAX} characters` }).refine((v) => !hasEmptySegment(v), {
|
|
15755
|
+
message: "must not start or end with a slash, or have two slashes together"
|
|
15756
|
+
}).refine((v) => !hasDotSegment(v), { message: 'must not have a "." or ".." folder in it' }).refine((v) => !hasPartialDoubleStar(v), { message: '"**" must stand alone between slashes' }).refine((v) => !hasForeignCharacter(v), {
|
|
15757
|
+
message: 'may use only letters, digits, ".", "_", "-" and "*" between slashes'
|
|
15758
|
+
}).refine((v) => !looksLikeSecret(v), {
|
|
15759
|
+
message: "this path looks like a credential; list a shorter folder above it"
|
|
15760
|
+
});
|
|
15761
|
+
var systemPartSchema = external_exports.object({
|
|
15762
|
+
id: systemPartIdSchema,
|
|
15763
|
+
name: descriptionText(SYSTEM_PART_NAME_MAX),
|
|
15764
|
+
description: descriptionText(SYSTEM_PART_DESCRIPTION_MAX),
|
|
15765
|
+
lane: systemLaneSchema,
|
|
15766
|
+
slot: external_exports.number({ error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }).int({ error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }).min(0, { error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }).max(SYSTEM_SLOT_MAX, { error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }),
|
|
15767
|
+
outside: external_exports.boolean(),
|
|
15768
|
+
owns: external_exports.array(systemOwnedPatternSchema).max(SYSTEM_OWNS_MAX, { message: `can list at most ${SYSTEM_OWNS_MAX} paths` })
|
|
15769
|
+
}).strict();
|
|
15770
|
+
var systemConnectionSchema = external_exports.object({
|
|
15771
|
+
from: systemPartIdSchema,
|
|
15772
|
+
to: systemPartIdSchema,
|
|
15773
|
+
kind: systemConnectionKindSchema,
|
|
15774
|
+
label: descriptionText(SYSTEM_CONNECTION_LABEL_MAX)
|
|
15775
|
+
}).strict();
|
|
15776
|
+
var systemDescriptionSchema = external_exports.object({
|
|
15777
|
+
format: external_exports.literal(SYSTEM_DESCRIPTION_FORMAT, { error: `must be ${SYSTEM_DESCRIPTION_FORMAT}` }),
|
|
15778
|
+
parts: external_exports.array(systemPartSchema).min(1, { message: "must list at least one part" }).max(SYSTEM_PARTS_MAX, { message: `can hold at most ${SYSTEM_PARTS_MAX} parts (five lanes of twelve slots)` }),
|
|
15779
|
+
connections: external_exports.array(systemConnectionSchema).max(SYSTEM_CONNECTIONS_MAX, { message: `can hold at most ${SYSTEM_CONNECTIONS_MAX} connections` })
|
|
15780
|
+
}).strict();
|
|
15781
|
+
var systemDescriptionProblemSchema = external_exports.object({
|
|
15782
|
+
path: external_exports.string(),
|
|
15783
|
+
message: external_exports.string()
|
|
15784
|
+
}).strict();
|
|
15785
|
+
var systemReportRecordedSchema = external_exports.object({
|
|
15786
|
+
outcome: external_exports.literal("recorded"),
|
|
15787
|
+
version: external_exports.number().int().min(1),
|
|
15788
|
+
reportedAt: utcIso8601
|
|
15789
|
+
}).strict();
|
|
15790
|
+
var systemReportRefusedSchema = external_exports.object({
|
|
15791
|
+
outcome: external_exports.literal("refused"),
|
|
15792
|
+
problems: external_exports.array(systemDescriptionProblemSchema).min(1)
|
|
15793
|
+
}).strict();
|
|
15794
|
+
var systemReportUnavailableSchema = external_exports.object({
|
|
15795
|
+
outcome: external_exports.literal("unavailable")
|
|
15796
|
+
}).strict();
|
|
15797
|
+
var systemReportResultSchema = external_exports.discriminatedUnion("outcome", [
|
|
15798
|
+
systemReportRecordedSchema,
|
|
15799
|
+
systemReportRefusedSchema,
|
|
15800
|
+
systemReportUnavailableSchema
|
|
15801
|
+
]);
|
|
15802
|
+
var SYSTEM_DRAW_SVG_MAX_BYTES = 2 * 1024 * 1024;
|
|
15803
|
+
var SYSTEM_DRAW_MERMAID_MAX_BYTES = 256 * 1024;
|
|
15804
|
+
var SYSTEM_DRAW_PNG_MAX_BYTES = 8 * 1024 * 1024;
|
|
15805
|
+
var systemDrawArchitectureRequestSchema = external_exports.object({
|
|
15806
|
+
picture: external_exports.literal("architecture"),
|
|
15807
|
+
version: external_exports.number().int().min(1)
|
|
15808
|
+
}).strict();
|
|
15809
|
+
var systemDrawPhaseRequestSchema = external_exports.object({
|
|
15810
|
+
picture: external_exports.literal("phase"),
|
|
15811
|
+
phase: phaseIdentitySchema,
|
|
15812
|
+
planVersion: external_exports.number().int().min(1)
|
|
15813
|
+
}).strict();
|
|
15814
|
+
var systemDrawRequestSchema = external_exports.discriminatedUnion("picture", [
|
|
15815
|
+
systemDrawArchitectureRequestSchema,
|
|
15816
|
+
systemDrawPhaseRequestSchema
|
|
15817
|
+
]);
|
|
15818
|
+
function utf8ByteLength2(text) {
|
|
15819
|
+
let bytes = 0;
|
|
15820
|
+
for (let i = 0; i < text.length; i++) {
|
|
15821
|
+
const unit = text.charCodeAt(i);
|
|
15822
|
+
if (unit < 128)
|
|
15823
|
+
bytes += 1;
|
|
15824
|
+
else if (unit < 2048)
|
|
15825
|
+
bytes += 2;
|
|
15826
|
+
else if (unit >= 55296 && unit <= 56319 && i + 1 < text.length) {
|
|
15827
|
+
const next = text.charCodeAt(i + 1);
|
|
15828
|
+
if (next >= 56320 && next <= 57343) {
|
|
15829
|
+
bytes += 4;
|
|
15830
|
+
i++;
|
|
15831
|
+
} else {
|
|
15832
|
+
bytes += 3;
|
|
15833
|
+
}
|
|
15834
|
+
} else
|
|
15835
|
+
bytes += 3;
|
|
15836
|
+
}
|
|
15837
|
+
return bytes;
|
|
15838
|
+
}
|
|
15839
|
+
var BASE64 = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
15840
|
+
function base64DecodedLength(text) {
|
|
15841
|
+
const padding = text.endsWith("==") ? 2 : text.endsWith("=") ? 1 : 0;
|
|
15842
|
+
return text.length / 4 * 3 - padding;
|
|
15843
|
+
}
|
|
15844
|
+
var systemPictureSchema = external_exports.object({
|
|
15845
|
+
svg: external_exports.string().min(1).refine((v) => utf8ByteLength2(v) <= SYSTEM_DRAW_SVG_MAX_BYTES, { message: "the picture is larger than 2 MB" }),
|
|
15846
|
+
png: external_exports.string().min(1).refine((v) => v.length % 4 === 0 && BASE64.test(v), { message: "the image is not valid base64" }).refine((v) => base64DecodedLength(v) <= SYSTEM_DRAW_PNG_MAX_BYTES, { message: "the image is larger than 8 MB" })
|
|
15847
|
+
}).strict();
|
|
15848
|
+
var systemDrawRefusedSchema = external_exports.object({
|
|
15849
|
+
outcome: external_exports.literal("refused"),
|
|
15850
|
+
reason: external_exports.string().min(1)
|
|
15851
|
+
}).strict();
|
|
15852
|
+
var systemDrawUnavailableSchema = external_exports.object({
|
|
15853
|
+
outcome: external_exports.literal("unavailable")
|
|
15854
|
+
}).strict();
|
|
15855
|
+
|
|
15856
|
+
// ../events/dist/phase-plan.js
|
|
15857
|
+
var PHASE_PLAN_FORMAT = 1;
|
|
15858
|
+
var PHASE_PLAN_CHANGES_MAX = 60;
|
|
15859
|
+
var PHASE_PLAN_SUMMARY_MAX = 160;
|
|
15860
|
+
var SCOPE_NOTE_MAX = 500;
|
|
15861
|
+
var PHASE_PLAN_PHASE_MAX = buildPhaseSchema.maxLength ?? 0;
|
|
15862
|
+
var PHASE_PLAN_CHANGE_KINDS = ["add", "change", "remove"];
|
|
15863
|
+
var phasePlanChangeSchema = external_exports.enum(PHASE_PLAN_CHANGE_KINDS, {
|
|
15864
|
+
error: `must be one of ${PHASE_PLAN_CHANGE_KINDS.join(", ")}`
|
|
15865
|
+
});
|
|
15866
|
+
var phasePlanPhaseSchema = phaseIdentitySchema.refine((v) => !isValidPhaseIdentity(v) || buildPhaseSchema.safeParse(String(v)).success, { message: `must be at most ${PHASE_PLAN_PHASE_MAX} characters` });
|
|
15867
|
+
var phasePlanSummarySchema = descriptionText(PHASE_PLAN_SUMMARY_MAX);
|
|
15868
|
+
var phasePlanEntrySchema = external_exports.object({
|
|
15869
|
+
part: systemPartIdSchema,
|
|
15870
|
+
change: phasePlanChangeSchema,
|
|
15871
|
+
summary: phasePlanSummarySchema
|
|
15872
|
+
}).strict();
|
|
15873
|
+
var phasePlanSchema = external_exports.object({
|
|
15874
|
+
format: external_exports.literal(PHASE_PLAN_FORMAT, { error: `must be ${PHASE_PLAN_FORMAT}` }),
|
|
15875
|
+
phase: phasePlanPhaseSchema,
|
|
15876
|
+
changes: external_exports.array(phasePlanEntrySchema).min(1, { message: "must list at least one part" }).max(PHASE_PLAN_CHANGES_MAX, { message: `can list at most ${PHASE_PLAN_CHANGES_MAX} parts` })
|
|
15877
|
+
}).strict();
|
|
15878
|
+
var scopePhaseSchema = buildPhaseSchema.refine((v) => isValidPhaseIdentity(v), {
|
|
15879
|
+
message: "must be a phase name"
|
|
15880
|
+
});
|
|
15881
|
+
var SCOPE_STATE_NAMES = [
|
|
15882
|
+
"not-planned",
|
|
15883
|
+
"waiting",
|
|
15884
|
+
"sent-back",
|
|
15885
|
+
"approved",
|
|
15886
|
+
"partly-stale",
|
|
15887
|
+
"withdrawn"
|
|
15888
|
+
];
|
|
15889
|
+
var scopeStateNameSchema = external_exports.enum(SCOPE_STATE_NAMES);
|
|
15890
|
+
var plannedScopeStateSchema = scopeStateNameSchema.exclude(["not-planned"]);
|
|
15891
|
+
var SCOPE_PART_STATUSES = ["approved", "waiting"];
|
|
15892
|
+
var scopePartStatusSchema = external_exports.enum(SCOPE_PART_STATUSES);
|
|
15893
|
+
var SCOPE_ACTS = ["approve", "send-back", "withdraw"];
|
|
15894
|
+
var scopeActSchema = external_exports.enum(SCOPE_ACTS);
|
|
15895
|
+
var SCOPE_GIVEN_IN = ["console", "chat"];
|
|
15896
|
+
var scopeGivenInSchema = external_exports.enum(SCOPE_GIVEN_IN);
|
|
15897
|
+
var scopeNoteSchema = descriptionText(SCOPE_NOTE_MAX);
|
|
15898
|
+
var scopeNoteWireSchema = external_exports.string().max(SCOPE_NOTE_MAX);
|
|
15899
|
+
var SCOPE_PART_VERDICTS = ["ok", "change"];
|
|
15900
|
+
var scopePartVerdictSchema = external_exports.enum(SCOPE_PART_VERDICTS);
|
|
15901
|
+
var scopeDecisionPartSchema = external_exports.object({
|
|
15902
|
+
part: systemPartIdSchema,
|
|
15903
|
+
verdict: scopePartVerdictSchema,
|
|
15904
|
+
note: scopeNoteWireSchema.optional()
|
|
15905
|
+
}).strict();
|
|
15906
|
+
var planVersionSchema = external_exports.number().int().min(1);
|
|
15907
|
+
var phasePlanRequestSchema = external_exports.object({
|
|
15908
|
+
descriptionVersion: external_exports.number().int().min(1),
|
|
15909
|
+
plan: external_exports.unknown()
|
|
15910
|
+
}).strict();
|
|
15911
|
+
var phasePlanRecordedSchema = external_exports.object({
|
|
15912
|
+
outcome: external_exports.literal("recorded"),
|
|
15913
|
+
version: planVersionSchema,
|
|
15914
|
+
phaseNumber: external_exports.number().int().min(1),
|
|
15915
|
+
state: plannedScopeStateSchema,
|
|
15916
|
+
changedParts: external_exports.array(systemPartIdSchema).max(PHASE_PLAN_CHANGES_MAX),
|
|
15917
|
+
typedApprovalOffered: external_exports.boolean(),
|
|
15918
|
+
reportedAt: utcIso8601
|
|
15919
|
+
}).strict();
|
|
15920
|
+
var phasePlanRefusedSchema = external_exports.object({
|
|
15921
|
+
outcome: external_exports.literal("refused"),
|
|
15922
|
+
problems: external_exports.array(systemDescriptionProblemSchema).min(1)
|
|
15923
|
+
}).strict();
|
|
15924
|
+
var phasePlanUnavailableSchema = external_exports.object({
|
|
15925
|
+
outcome: external_exports.literal("unavailable")
|
|
15926
|
+
}).strict();
|
|
15927
|
+
var phasePlanResultSchema = external_exports.discriminatedUnion("outcome", [
|
|
15928
|
+
phasePlanRecordedSchema,
|
|
15929
|
+
phasePlanRefusedSchema,
|
|
15930
|
+
phasePlanUnavailableSchema
|
|
15931
|
+
]);
|
|
15932
|
+
var scopeApproveRequestSchema = external_exports.object({
|
|
15933
|
+
act: external_exports.literal("approve"),
|
|
15934
|
+
planVersion: planVersionSchema,
|
|
15935
|
+
parts: external_exports.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX)
|
|
15936
|
+
}).strict();
|
|
15937
|
+
var scopeSendBackRequestSchema = external_exports.object({
|
|
15938
|
+
act: external_exports.literal("send-back"),
|
|
15939
|
+
planVersion: planVersionSchema,
|
|
15940
|
+
parts: external_exports.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX),
|
|
15941
|
+
note: scopeNoteWireSchema.optional()
|
|
15942
|
+
}).strict();
|
|
15943
|
+
var scopeWithdrawRequestSchema = external_exports.object({
|
|
15944
|
+
act: external_exports.literal("withdraw"),
|
|
15945
|
+
planVersion: planVersionSchema
|
|
15946
|
+
}).strict();
|
|
15947
|
+
var scopeDecisionRequestSchema = external_exports.discriminatedUnion("act", [
|
|
15948
|
+
scopeApproveRequestSchema,
|
|
15949
|
+
scopeSendBackRequestSchema,
|
|
15950
|
+
scopeWithdrawRequestSchema
|
|
15951
|
+
]);
|
|
15952
|
+
var scopeDecisionRecordedSchema = external_exports.object({
|
|
15953
|
+
outcome: external_exports.literal("recorded"),
|
|
15954
|
+
act: scopeActSchema,
|
|
15955
|
+
phase: scopePhaseSchema,
|
|
15956
|
+
planVersion: planVersionSchema,
|
|
15957
|
+
state: plannedScopeStateSchema,
|
|
15958
|
+
decidedAt: utcIso8601
|
|
15959
|
+
}).strict();
|
|
15960
|
+
var scopeDecisionAlreadyApprovedSchema = external_exports.object({
|
|
15961
|
+
outcome: external_exports.literal("already-approved"),
|
|
15962
|
+
replanNeeded: external_exports.boolean(),
|
|
15963
|
+
reason: external_exports.string().min(1)
|
|
15964
|
+
}).strict();
|
|
15965
|
+
var scopeDecisionRefusedSchema = external_exports.object({
|
|
15966
|
+
outcome: external_exports.literal("refused"),
|
|
15967
|
+
problems: external_exports.array(systemDescriptionProblemSchema).min(1)
|
|
15968
|
+
}).strict();
|
|
15969
|
+
var scopeDecisionUnavailableSchema = external_exports.object({
|
|
15970
|
+
outcome: external_exports.literal("unavailable")
|
|
15971
|
+
}).strict();
|
|
15972
|
+
function reasonOutcome(outcome) {
|
|
15973
|
+
return external_exports.object({ outcome: external_exports.literal(outcome), reason: external_exports.string().min(1) }).strict();
|
|
15974
|
+
}
|
|
15975
|
+
var scopeDecisionResultSchema = external_exports.discriminatedUnion("outcome", [
|
|
15976
|
+
scopeDecisionRecordedSchema,
|
|
15977
|
+
scopeDecisionAlreadyApprovedSchema,
|
|
15978
|
+
scopeDecisionRefusedSchema,
|
|
15979
|
+
scopeDecisionUnavailableSchema,
|
|
15980
|
+
reasonOutcome("not-owner"),
|
|
15981
|
+
reasonOutcome("no-owner"),
|
|
15982
|
+
reasonOutcome("plan-changed"),
|
|
15983
|
+
reasonOutcome("nothing-to-withdraw"),
|
|
15984
|
+
reasonOutcome("build-started")
|
|
15985
|
+
]);
|
|
15986
|
+
var scopeReadPartSchema = external_exports.object({
|
|
15987
|
+
part: systemPartIdSchema,
|
|
15988
|
+
name: systemPartSchema.shape.name,
|
|
15989
|
+
change: phasePlanChangeSchema,
|
|
15990
|
+
summary: phasePlanSummarySchema,
|
|
15991
|
+
status: scopePartStatusSchema,
|
|
15992
|
+
changed: external_exports.boolean()
|
|
15993
|
+
}).strict();
|
|
15994
|
+
var scopeReadDecisionSchema = external_exports.object({
|
|
15995
|
+
act: scopeActSchema,
|
|
15996
|
+
planVersion: planVersionSchema,
|
|
15997
|
+
givenIn: scopeGivenInSchema,
|
|
15998
|
+
at: utcIso8601,
|
|
15999
|
+
parts: external_exports.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX),
|
|
16000
|
+
note: scopeNoteWireSchema.optional()
|
|
16001
|
+
}).strict();
|
|
16002
|
+
var scopeReadNotPlannedSchema = external_exports.object({
|
|
16003
|
+
phase: scopePhaseSchema,
|
|
16004
|
+
state: external_exports.literal("not-planned"),
|
|
16005
|
+
canDecide: external_exports.boolean()
|
|
16006
|
+
}).strict();
|
|
16007
|
+
var scopeReadPlannedSchema = external_exports.object({
|
|
16008
|
+
phase: scopePhaseSchema,
|
|
16009
|
+
state: plannedScopeStateSchema,
|
|
16010
|
+
phaseNumber: external_exports.number().int().min(1),
|
|
16011
|
+
planVersion: planVersionSchema,
|
|
16012
|
+
parts: external_exports.array(scopeReadPartSchema).min(1).max(PHASE_PLAN_CHANGES_MAX),
|
|
16013
|
+
decisions: external_exports.array(scopeReadDecisionSchema),
|
|
16014
|
+
canDecide: external_exports.boolean(),
|
|
16015
|
+
replanLine: external_exports.string().min(1).optional()
|
|
16016
|
+
}).strict();
|
|
16017
|
+
var scopeReadSchema = external_exports.union([scopeReadNotPlannedSchema, scopeReadPlannedSchema]);
|
|
16018
|
+
|
|
16019
|
+
// ../events/dist/client-version.js
|
|
16020
|
+
var CLIENT_VERSION_HEADER = "x-halfcycle-client";
|
|
16021
|
+
var TYPED_APPROVAL_OFF_SENTENCE = "Approving by typing is switched off for now.";
|
|
16022
|
+
var CLIENT_UPDATE_SENTENCE = `To approve by typing in a session, Halfcycle needs updating: run \`${HALFCYCLE_COMMAND}\` in this project's folder, then reopen your session and ask again.`;
|
|
16023
|
+
|
|
16024
|
+
// ../events/dist/scope-notices.js
|
|
16025
|
+
var scopePartNameSchema = external_exports.string().min(1).max(SYSTEM_PART_NAME_MAX);
|
|
16026
|
+
var scopeNoteEntrySchema = external_exports.object({
|
|
16027
|
+
part: scopePartNameSchema.optional(),
|
|
16028
|
+
note: external_exports.string().min(1).max(SCOPE_NOTE_MAX)
|
|
16029
|
+
}).strict();
|
|
16030
|
+
var scopeSentBackSchema = external_exports.object({
|
|
16031
|
+
phase: scopePhaseSchema,
|
|
16032
|
+
number: external_exports.number().int().min(1),
|
|
16033
|
+
notes: external_exports.array(scopeNoteEntrySchema).min(1).max(PHASE_PLAN_CHANGES_MAX + 1)
|
|
16034
|
+
}).strict().superRefine((entry, ctx) => {
|
|
16035
|
+
entry.notes.forEach((n, i) => {
|
|
16036
|
+
if (n.part === void 0 && i !== entry.notes.length - 1) {
|
|
16037
|
+
ctx.addIssue({
|
|
16038
|
+
code: external_exports.ZodIssueCode.custom,
|
|
16039
|
+
path: ["notes", i],
|
|
16040
|
+
message: "the note on the whole phase comes after every note on a part"
|
|
16041
|
+
});
|
|
16042
|
+
}
|
|
16043
|
+
});
|
|
16044
|
+
});
|
|
16045
|
+
var scopeWaitingSchema = external_exports.object({
|
|
16046
|
+
phase: scopePhaseSchema,
|
|
16047
|
+
number: external_exports.number().int().min(1),
|
|
16048
|
+
changedParts: external_exports.array(scopePartNameSchema).max(PHASE_PLAN_CHANGES_MAX)
|
|
16049
|
+
}).strict();
|
|
16050
|
+
var scopeNoticesSchema = external_exports.object({
|
|
16051
|
+
sentBack: external_exports.array(scopeSentBackSchema),
|
|
16052
|
+
waiting: external_exports.array(scopeWaitingSchema),
|
|
16053
|
+
typedApprovalOffered: external_exports.boolean()
|
|
16054
|
+
}).strict().refine((v) => v.sentBack.length > 0 || v.waiting.length > 0, {
|
|
16055
|
+
message: "carried only when a phase was sent back or waits for the owner"
|
|
16056
|
+
});
|
|
16057
|
+
function quotedParts(parts) {
|
|
16058
|
+
return parts.map((p) => `"${p}"`).join(", ");
|
|
16059
|
+
}
|
|
16060
|
+
function approvalSentence(phase, offerTyping) {
|
|
16061
|
+
if (offerTyping === "off")
|
|
16062
|
+
return TYPED_APPROVAL_OFF_SENTENCE;
|
|
16063
|
+
if (offerTyping === "update")
|
|
16064
|
+
return CLIENT_UPDATE_SENTENCE;
|
|
16065
|
+
return `In their own Claude Code session, they ask to see the plan, check each part, and then type: approve ${phase}`;
|
|
16066
|
+
}
|
|
16067
|
+
function sentBackNotice(entry) {
|
|
16068
|
+
return [
|
|
16069
|
+
`Before anything else: the project's owner sent the scope of phase ${entry.number}, "${entry.phase}", back with these notes.`,
|
|
16070
|
+
...entry.notes.map((n) => n.part === void 0 ? `- ${n.note}` : `- "${n.part}": ${n.note}`),
|
|
16071
|
+
`Change the plan to answer every note, then send it again with \`${HALFCYCLE_COMMAND} plan ${quoteForShell(entry.phase)}\`.`
|
|
16072
|
+
].join("\n");
|
|
16073
|
+
}
|
|
16074
|
+
function waitingNotice(entry, offerTyping) {
|
|
16075
|
+
return [
|
|
16076
|
+
...entry.changedParts.length > 0 ? [`These parts changed since the owner last decided on this phase, and wait for their approval: ${quotedParts(entry.changedParts)}.`] : [],
|
|
16077
|
+
`The scope of phase ${entry.number}, "${entry.phase}", is waiting for the project's owner to approve it.`,
|
|
16078
|
+
approvalSentence(entry.phase, offerTyping)
|
|
16079
|
+
].join(" ");
|
|
16080
|
+
}
|
|
16081
|
+
function scopeNoticeText(scope, options) {
|
|
16082
|
+
const omit2 = options.omitActivePhase === true ? options.activePhase : void 0;
|
|
16083
|
+
return {
|
|
16084
|
+
sentBack: scope.sentBack.map(sentBackNotice),
|
|
16085
|
+
waiting: scope.waiting.filter((w) => omit2 === void 0 || w.phase !== omit2).map((w) => waitingNotice(w, options.offerTyping))
|
|
16086
|
+
};
|
|
16087
|
+
}
|
|
16088
|
+
|
|
15298
16089
|
// ../events/dist/credential-wire.js
|
|
15299
16090
|
var engagementCredentialResponseSchema = external_exports.object({
|
|
15300
16091
|
engagementId: external_exports.string(),
|
|
@@ -15324,12 +16115,60 @@ var NOT_THIS_ACCOUNT_SENTENCE = `Halfcycle is not connected: this project belong
|
|
|
15324
16115
|
var UPDATE_CLAUDE_CODE_SENTENCE = "Halfcycle is not connected: this Claude Code version does not name the server it connects to, so no credential was sent. Update Claude Code, then reopen.";
|
|
15325
16116
|
var CANNOT_REACH_SENTENCE = "Halfcycle is not connected: its server could not be reached, so its tools are unavailable in this session. Check your network, then reopen the project.";
|
|
15326
16117
|
var SERVICE_PROBLEM_SENTENCE = "Halfcycle is not connected: its service had a problem just now, so nothing in this session is being checked. Reopen the project to try again.";
|
|
16118
|
+
var SCOPE_UNCHECKED_SENTENCE = "If this project needs approval before building, that cannot be checked either, so work done now shows as built without approval unless already approved.";
|
|
15327
16119
|
var PLACEHOLDER_SENTENCES = Object.freeze({
|
|
15328
16120
|
[NO_CREDENTIAL_PLACEHOLDER]: NO_CREDENTIAL_SENTENCE,
|
|
15329
16121
|
[NOT_THIS_ACCOUNT_PLACEHOLDER]: NOT_THIS_ACCOUNT_SENTENCE,
|
|
15330
16122
|
[UPDATE_CLAUDE_CODE_PLACEHOLDER]: UPDATE_CLAUDE_CODE_SENTENCE
|
|
15331
16123
|
});
|
|
15332
16124
|
|
|
16125
|
+
// ../events/dist/scope-chat.js
|
|
16126
|
+
var chatScopeApproveRequestSchema = external_exports.object({
|
|
16127
|
+
act: external_exports.literal("approve"),
|
|
16128
|
+
reference: phaseIdentitySchema,
|
|
16129
|
+
planVersion: planVersionSchema
|
|
16130
|
+
}).strict();
|
|
16131
|
+
var chatScopeSendBackRequestSchema = external_exports.object({
|
|
16132
|
+
act: external_exports.literal("send-back"),
|
|
16133
|
+
reference: phaseIdentitySchema,
|
|
16134
|
+
planVersion: planVersionSchema,
|
|
16135
|
+
note: scopeNoteWireSchema.optional()
|
|
16136
|
+
}).strict();
|
|
16137
|
+
var chatScopeRequestSchema = external_exports.discriminatedUnion("act", [
|
|
16138
|
+
chatScopeApproveRequestSchema,
|
|
16139
|
+
chatScopeSendBackRequestSchema
|
|
16140
|
+
]);
|
|
16141
|
+
var CHAT_SCOPE_ACTS = ["approve", "send-back"];
|
|
16142
|
+
var chatScopeRecordedSchema = external_exports.object({
|
|
16143
|
+
outcome: external_exports.literal("recorded"),
|
|
16144
|
+
act: external_exports.enum(CHAT_SCOPE_ACTS),
|
|
16145
|
+
phase: scopePhaseSchema,
|
|
16146
|
+
planVersion: planVersionSchema
|
|
16147
|
+
}).strict();
|
|
16148
|
+
var chatScopeAlreadyApprovedSchema = external_exports.object({
|
|
16149
|
+
outcome: external_exports.literal("already-approved"),
|
|
16150
|
+
replanNeeded: external_exports.boolean()
|
|
16151
|
+
}).strict();
|
|
16152
|
+
var chatScopeRefusedSchema = external_exports.object({
|
|
16153
|
+
outcome: external_exports.literal("refused"),
|
|
16154
|
+
problems: external_exports.array(systemDescriptionProblemSchema).min(1)
|
|
16155
|
+
}).strict();
|
|
16156
|
+
function bareOutcome(outcome) {
|
|
16157
|
+
return external_exports.object({ outcome: external_exports.literal(outcome) }).strict();
|
|
16158
|
+
}
|
|
16159
|
+
var chatScopeResultSchema = external_exports.discriminatedUnion("outcome", [
|
|
16160
|
+
chatScopeRecordedSchema,
|
|
16161
|
+
chatScopeAlreadyApprovedSchema,
|
|
16162
|
+
chatScopeRefusedSchema,
|
|
16163
|
+
bareOutcome("show-first"),
|
|
16164
|
+
bareOutcome("plan-changed"),
|
|
16165
|
+
bareOutcome("build-started"),
|
|
16166
|
+
bareOutcome("not-owner"),
|
|
16167
|
+
bareOutcome("no-owner"),
|
|
16168
|
+
bareOutcome("not-offered"),
|
|
16169
|
+
bareOutcome("unavailable")
|
|
16170
|
+
]);
|
|
16171
|
+
|
|
15333
16172
|
// dist/hook-protocol.js
|
|
15334
16173
|
import { writeSync } from "node:fs";
|
|
15335
16174
|
function emitBlock(reason) {
|
|
@@ -15915,16 +16754,16 @@ function detectEnvReads(diffContent, jsComments) {
|
|
|
15915
16754
|
if (raw.startsWith("-") && !raw.startsWith("---"))
|
|
15916
16755
|
continue;
|
|
15917
16756
|
const isSource = raw.startsWith("+") && !raw.startsWith("+++") || raw.startsWith(" ");
|
|
15918
|
-
const
|
|
15919
|
-
const comment = jsComments && isSource ? commentedOut(
|
|
16757
|
+
const line2 = jsComments && isSource ? raw.slice(1) : raw;
|
|
16758
|
+
const comment = jsComments && isSource ? commentedOut(line2) : void 0;
|
|
15920
16759
|
const live = (match) => comment === void 0 || !comment(match.index, match.index + match[0].length);
|
|
15921
|
-
for (const match of
|
|
16760
|
+
for (const match of line2.matchAll(dotPattern))
|
|
15922
16761
|
if (live(match))
|
|
15923
16762
|
found.add(match[1]);
|
|
15924
|
-
for (const match of
|
|
16763
|
+
for (const match of line2.matchAll(bracketPattern))
|
|
15925
16764
|
if (live(match))
|
|
15926
16765
|
found.add(match[1]);
|
|
15927
|
-
for (const destructure of
|
|
16766
|
+
for (const destructure of line2.matchAll(destructurePattern)) {
|
|
15928
16767
|
if (!live(destructure))
|
|
15929
16768
|
continue;
|
|
15930
16769
|
for (const part of destructure[1].split(",")) {
|
|
@@ -15937,25 +16776,25 @@ function detectEnvReads(diffContent, jsComments) {
|
|
|
15937
16776
|
return found;
|
|
15938
16777
|
}
|
|
15939
16778
|
var LINE_TERMINATOR = /[\r\u2028\u2029]/g;
|
|
15940
|
-
function commentedOut(
|
|
15941
|
-
const lead =
|
|
16779
|
+
function commentedOut(line2) {
|
|
16780
|
+
const lead = line2.length - line2.trimStart().length;
|
|
15942
16781
|
let body;
|
|
15943
16782
|
let close;
|
|
15944
16783
|
let expression;
|
|
15945
|
-
if (
|
|
16784
|
+
if (line2.startsWith("//", lead)) {
|
|
15946
16785
|
body = lead + 2;
|
|
15947
16786
|
LINE_TERMINATOR.lastIndex = body;
|
|
15948
|
-
close = LINE_TERMINATOR.exec(
|
|
16787
|
+
close = LINE_TERMINATOR.exec(line2)?.index ?? line2.length;
|
|
15949
16788
|
expression = "{";
|
|
15950
|
-
} else if (
|
|
15951
|
-
body = lead + (
|
|
15952
|
-
const found =
|
|
15953
|
-
close = found === -1 ?
|
|
15954
|
-
expression =
|
|
16789
|
+
} else if (line2.startsWith("/*", lead) || line2.startsWith("{/*", lead)) {
|
|
16790
|
+
body = lead + (line2[lead] === "{" ? 3 : 2);
|
|
16791
|
+
const found = line2.indexOf("*/", body);
|
|
16792
|
+
close = found === -1 ? line2.length : found;
|
|
16793
|
+
expression = line2[lead] === "{" ? "${" : "{";
|
|
15955
16794
|
} else {
|
|
15956
16795
|
return void 0;
|
|
15957
16796
|
}
|
|
15958
|
-
const opened =
|
|
16797
|
+
const opened = line2.indexOf(expression, body);
|
|
15959
16798
|
return (start, end) => start >= body && end <= close && (opened === -1 || opened >= start || opened >= close);
|
|
15960
16799
|
}
|
|
15961
16800
|
function collectDeclarationFiles(context) {
|
|
@@ -16034,8 +16873,8 @@ function flyTomlSectionHas(name, content, sectionHeader) {
|
|
|
16034
16873
|
const lines = content.split("\n");
|
|
16035
16874
|
let inSection = false;
|
|
16036
16875
|
const keyPattern = new RegExp(`^${name}\\s*=`);
|
|
16037
|
-
for (const
|
|
16038
|
-
const trimmed =
|
|
16876
|
+
for (const line2 of lines) {
|
|
16877
|
+
const trimmed = line2.trim();
|
|
16039
16878
|
if (trimmed === sectionHeader) {
|
|
16040
16879
|
inSection = true;
|
|
16041
16880
|
continue;
|
|
@@ -16086,10 +16925,10 @@ function extractSqlWrites(diffContent) {
|
|
|
16086
16925
|
seen.add(key);
|
|
16087
16926
|
writes.push(write);
|
|
16088
16927
|
};
|
|
16089
|
-
for (const
|
|
16090
|
-
if (isRemovedLine(
|
|
16928
|
+
for (const line2 of diffContent.split("\n")) {
|
|
16929
|
+
if (isRemovedLine(line2) || isContextLine(line2) || isDiffHeader(line2))
|
|
16091
16930
|
continue;
|
|
16092
|
-
const code = stripComments(stripDiffPrefix(
|
|
16931
|
+
const code = stripComments(stripDiffPrefix(line2));
|
|
16093
16932
|
push(parseRawSql(code));
|
|
16094
16933
|
push(parseQueryBuilder(code));
|
|
16095
16934
|
push(parseOrmCall(code));
|
|
@@ -16099,23 +16938,23 @@ function extractSqlWrites(diffContent) {
|
|
|
16099
16938
|
}
|
|
16100
16939
|
return writes;
|
|
16101
16940
|
}
|
|
16102
|
-
function isRemovedLine(
|
|
16103
|
-
return
|
|
16941
|
+
function isRemovedLine(line2) {
|
|
16942
|
+
return line2.startsWith("-") && !line2.startsWith("---");
|
|
16104
16943
|
}
|
|
16105
|
-
function isContextLine(
|
|
16106
|
-
return
|
|
16944
|
+
function isContextLine(line2) {
|
|
16945
|
+
return line2.startsWith(" ");
|
|
16107
16946
|
}
|
|
16108
|
-
function isDiffHeader(
|
|
16109
|
-
return
|
|
16947
|
+
function isDiffHeader(line2) {
|
|
16948
|
+
return line2.startsWith("+++") || line2.startsWith("---");
|
|
16110
16949
|
}
|
|
16111
|
-
function stripDiffPrefix(
|
|
16112
|
-
if (
|
|
16113
|
-
return
|
|
16114
|
-
if (
|
|
16115
|
-
return
|
|
16116
|
-
if (
|
|
16117
|
-
return
|
|
16118
|
-
return
|
|
16950
|
+
function stripDiffPrefix(line2) {
|
|
16951
|
+
if (line2.startsWith("+") && !line2.startsWith("+++"))
|
|
16952
|
+
return line2.slice(1);
|
|
16953
|
+
if (line2.startsWith("-") && !line2.startsWith("---"))
|
|
16954
|
+
return line2.slice(1);
|
|
16955
|
+
if (line2.startsWith(" "))
|
|
16956
|
+
return line2.slice(1);
|
|
16957
|
+
return line2;
|
|
16119
16958
|
}
|
|
16120
16959
|
function assembleStatements(diffContent) {
|
|
16121
16960
|
const statements = [];
|
|
@@ -16128,16 +16967,16 @@ function assembleStatements(diffContent) {
|
|
|
16128
16967
|
buffer = [];
|
|
16129
16968
|
hasAddedLine = false;
|
|
16130
16969
|
};
|
|
16131
|
-
for (const
|
|
16132
|
-
if (
|
|
16970
|
+
for (const line2 of diffContent.split("\n")) {
|
|
16971
|
+
if (line2.startsWith("@@") || isDiffHeader(line2)) {
|
|
16133
16972
|
flush(true);
|
|
16134
16973
|
continue;
|
|
16135
16974
|
}
|
|
16136
|
-
if (isRemovedLine(
|
|
16975
|
+
if (isRemovedLine(line2)) {
|
|
16137
16976
|
flush(false);
|
|
16138
16977
|
continue;
|
|
16139
16978
|
}
|
|
16140
|
-
const rawContent = stripDiffPrefix(
|
|
16979
|
+
const rawContent = stripDiffPrefix(line2);
|
|
16141
16980
|
if (rawContent.trim() === "") {
|
|
16142
16981
|
flush(true);
|
|
16143
16982
|
continue;
|
|
@@ -16145,7 +16984,7 @@ function assembleStatements(diffContent) {
|
|
|
16145
16984
|
const content = stripComments(rawContent);
|
|
16146
16985
|
if (content.trim() === "")
|
|
16147
16986
|
continue;
|
|
16148
|
-
if (
|
|
16987
|
+
if (line2.startsWith("+"))
|
|
16149
16988
|
hasAddedLine = true;
|
|
16150
16989
|
buffer.push(content.trim());
|
|
16151
16990
|
if (content.includes(";"))
|
|
@@ -16162,44 +17001,44 @@ function normaliseTable(raw) {
|
|
|
16162
17001
|
return name.toLowerCase();
|
|
16163
17002
|
}
|
|
16164
17003
|
var TABLE_IDENT = String.raw`((?:"[^"]+"|[a-zA-Z_][a-zA-Z0-9_]*)(?:\.(?:"[^"]+"|[a-zA-Z_][a-zA-Z0-9_]*))?)`;
|
|
16165
|
-
function parseRawSql(
|
|
16166
|
-
if (/\bON\s+CONFLICT\b/i.test(
|
|
16167
|
-
const m =
|
|
17004
|
+
function parseRawSql(line2) {
|
|
17005
|
+
if (/\bON\s+CONFLICT\b/i.test(line2)) {
|
|
17006
|
+
const m = line2.match(new RegExp(String.raw`INSERT\s+INTO\s+${TABLE_IDENT}`, "i"));
|
|
16168
17007
|
if (m)
|
|
16169
17008
|
return { kind: "upsert", table: normaliseTable(m[1]), whereColumns: [] };
|
|
16170
17009
|
}
|
|
16171
|
-
const upsertM =
|
|
17010
|
+
const upsertM = line2.match(new RegExp(String.raw`UPSERT\s+INTO\s+${TABLE_IDENT}`, "i"));
|
|
16172
17011
|
if (upsertM)
|
|
16173
17012
|
return { kind: "upsert", table: normaliseTable(upsertM[1]), whereColumns: [] };
|
|
16174
|
-
const insertM =
|
|
17013
|
+
const insertM = line2.match(new RegExp(String.raw`INSERT\s+INTO\s+${TABLE_IDENT}`, "i"));
|
|
16175
17014
|
if (insertM)
|
|
16176
17015
|
return { kind: "insert", table: normaliseTable(insertM[1]), whereColumns: [] };
|
|
16177
|
-
const updateM =
|
|
17016
|
+
const updateM = line2.match(new RegExp(String.raw`\bUPDATE\s+${TABLE_IDENT}\s+[\s\S]*?\bSET\b`, "i"));
|
|
16178
17017
|
if (updateM) {
|
|
16179
17018
|
return {
|
|
16180
17019
|
kind: "update",
|
|
16181
17020
|
table: normaliseTable(updateM[1]),
|
|
16182
|
-
whereColumns: extractWhereColumns(
|
|
17021
|
+
whereColumns: extractWhereColumns(line2)
|
|
16183
17022
|
};
|
|
16184
17023
|
}
|
|
16185
|
-
const deleteM =
|
|
17024
|
+
const deleteM = line2.match(new RegExp(String.raw`DELETE\s+FROM\s+${TABLE_IDENT}`, "i"));
|
|
16186
17025
|
if (deleteM) {
|
|
16187
17026
|
return {
|
|
16188
17027
|
kind: "delete",
|
|
16189
17028
|
table: normaliseTable(deleteM[1]),
|
|
16190
|
-
whereColumns: extractWhereColumns(
|
|
17029
|
+
whereColumns: extractWhereColumns(line2)
|
|
16191
17030
|
};
|
|
16192
17031
|
}
|
|
16193
17032
|
return null;
|
|
16194
17033
|
}
|
|
16195
17034
|
function parseQueryBuilder(rawLine) {
|
|
16196
|
-
const
|
|
16197
|
-
const m =
|
|
17035
|
+
const line2 = stripDiffPrefix(rawLine);
|
|
17036
|
+
const m = line2.match(/(?<!\bcreate(?:Hash|Hmac|Sign|Verify|Cipheriv|Decipheriv)\s*\([^)]*)['"]([\w]+)['"]\s*\)\s*\.(insert|update|upsert|delete)\s*\(/i);
|
|
16198
17037
|
if (m) {
|
|
16199
17038
|
const kind = m[2].toLowerCase();
|
|
16200
17039
|
return { kind, table: m[1].toLowerCase(), whereColumns: [] };
|
|
16201
17040
|
}
|
|
16202
|
-
const fromM =
|
|
17041
|
+
const fromM = line2.match(/\.(?:from|into|table)\s*\(\s*['"]([\w]+)['"]\s*\)\s*\.(insert|update|upsert|delete)\s*\(/i);
|
|
16203
17042
|
if (fromM) {
|
|
16204
17043
|
const kind = fromM[2].toLowerCase();
|
|
16205
17044
|
return { kind, table: fromM[1].toLowerCase(), whereColumns: [] };
|
|
@@ -16223,11 +17062,11 @@ var TYPEORM_KIND = {
|
|
|
16223
17062
|
delete: "delete"
|
|
16224
17063
|
};
|
|
16225
17064
|
function parseOrmCall(rawLine) {
|
|
16226
|
-
const
|
|
16227
|
-
return parsePrisma(
|
|
17065
|
+
const line2 = stripDiffPrefix(rawLine);
|
|
17066
|
+
return parsePrisma(line2) ?? parseTypeorm(line2) ?? parseDrizzle(line2);
|
|
16228
17067
|
}
|
|
16229
|
-
function parsePrisma(
|
|
16230
|
-
const m =
|
|
17068
|
+
function parsePrisma(line2) {
|
|
17069
|
+
const m = line2.match(/\b[\w$]+\.([a-zA-Z_$][\w$]*)\.(create|createMany|update|updateMany|upsert|delete|deleteMany)\s*\(/);
|
|
16231
17070
|
if (!m)
|
|
16232
17071
|
return null;
|
|
16233
17072
|
const kind = PRISMA_KIND[m[2].toLowerCase()];
|
|
@@ -16235,29 +17074,29 @@ function parsePrisma(line) {
|
|
|
16235
17074
|
return null;
|
|
16236
17075
|
return { kind, table: m[1].toLowerCase(), whereColumns: [] };
|
|
16237
17076
|
}
|
|
16238
|
-
function parseDrizzle(
|
|
16239
|
-
const upd =
|
|
17077
|
+
function parseDrizzle(line2) {
|
|
17078
|
+
const upd = line2.match(/\b[\w$]+\.update\s*\(\s*['"]?([\w$]+)['"]?\s*\)\s*\.set\s*\(/);
|
|
16240
17079
|
if (upd)
|
|
16241
17080
|
return { kind: "update", table: upd[1].toLowerCase(), whereColumns: [] };
|
|
16242
|
-
const ins =
|
|
17081
|
+
const ins = line2.match(/\b[\w$]+\.insert\s*\(\s*['"]?([\w$]+)['"]?\s*\)/);
|
|
16243
17082
|
if (ins)
|
|
16244
17083
|
return { kind: "insert", table: ins[1].toLowerCase(), whereColumns: [] };
|
|
16245
|
-
const del =
|
|
17084
|
+
const del = line2.match(/\b[\w$]+\.delete\s*\(\s*['"]?([\w$]+)['"]?\s*\)/);
|
|
16246
17085
|
if (del)
|
|
16247
17086
|
return { kind: "delete", table: del[1].toLowerCase(), whereColumns: [] };
|
|
16248
17087
|
return null;
|
|
16249
17088
|
}
|
|
16250
|
-
function parseTypeorm(
|
|
16251
|
-
const qb =
|
|
17089
|
+
function parseTypeorm(line2) {
|
|
17090
|
+
const qb = line2.match(/\.createQueryBuilder\s*\([^)]*\)[\s\S]*?\.update\s*\(\s*([a-zA-Z_$][\w$]*)/);
|
|
16252
17091
|
if (qb)
|
|
16253
17092
|
return { kind: "update", table: qb[1].toLowerCase(), whereColumns: [] };
|
|
16254
|
-
const saveUpsert =
|
|
17093
|
+
const saveUpsert = line2.match(/(?:^|[^.\w$])([a-zA-Z_$][\w$]*)\.(save|upsert)\s*\(/);
|
|
16255
17094
|
if (saveUpsert) {
|
|
16256
17095
|
const kind = TYPEORM_KIND[saveUpsert[2].toLowerCase()];
|
|
16257
17096
|
if (kind)
|
|
16258
17097
|
return { kind, table: saveUpsert[1].toLowerCase(), whereColumns: [] };
|
|
16259
17098
|
}
|
|
16260
|
-
const repo =
|
|
17099
|
+
const repo = line2.match(/(?:^|[^.\w$])([a-zA-Z_$][\w$]*(?:repo|repository))\.(insert|update|delete)\s*\(/i);
|
|
16261
17100
|
if (repo) {
|
|
16262
17101
|
const kind = TYPEORM_KIND[repo[2].toLowerCase()];
|
|
16263
17102
|
if (kind)
|
|
@@ -16265,8 +17104,8 @@ function parseTypeorm(line) {
|
|
|
16265
17104
|
}
|
|
16266
17105
|
return null;
|
|
16267
17106
|
}
|
|
16268
|
-
function extractWhereColumns(
|
|
16269
|
-
const whereM =
|
|
17107
|
+
function extractWhereColumns(line2) {
|
|
17108
|
+
const whereM = line2.match(/\bWHERE\s+(.+)/i);
|
|
16270
17109
|
if (!whereM)
|
|
16271
17110
|
return [];
|
|
16272
17111
|
const fragment = whereM[1].replace(/\s+(?:ORDER|LIMIT|GROUP|HAVING)\b.*/i, "");
|
|
@@ -16282,17 +17121,17 @@ function extractSchemaConstraints(diffContent) {
|
|
|
16282
17121
|
const results = [];
|
|
16283
17122
|
const seen = /* @__PURE__ */ new Set();
|
|
16284
17123
|
let currentTable;
|
|
16285
|
-
for (const
|
|
16286
|
-
if (
|
|
17124
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17125
|
+
if (line2.startsWith("-") && !line2.startsWith("---")) {
|
|
16287
17126
|
currentTable = void 0;
|
|
16288
17127
|
continue;
|
|
16289
17128
|
}
|
|
16290
|
-
if (
|
|
17129
|
+
if (line2.startsWith("@@")) {
|
|
16291
17130
|
currentTable = void 0;
|
|
16292
17131
|
continue;
|
|
16293
17132
|
}
|
|
16294
|
-
const isAdded =
|
|
16295
|
-
const raw = stripDiffPrefix2(
|
|
17133
|
+
const isAdded = line2.startsWith("+") && !line2.startsWith("+++");
|
|
17134
|
+
const raw = stripDiffPrefix2(line2);
|
|
16296
17135
|
if (raw.trim() === "") {
|
|
16297
17136
|
currentTable = void 0;
|
|
16298
17137
|
continue;
|
|
@@ -16316,22 +17155,22 @@ function extractSchemaConstraints(diffContent) {
|
|
|
16316
17155
|
}
|
|
16317
17156
|
return results;
|
|
16318
17157
|
}
|
|
16319
|
-
function stripDiffPrefix2(
|
|
16320
|
-
if (
|
|
16321
|
-
return
|
|
16322
|
-
if (
|
|
16323
|
-
return
|
|
16324
|
-
return
|
|
17158
|
+
function stripDiffPrefix2(line2) {
|
|
17159
|
+
if (line2.startsWith("+") && !line2.startsWith("+++"))
|
|
17160
|
+
return line2.slice(1);
|
|
17161
|
+
if (line2.startsWith(" "))
|
|
17162
|
+
return line2.slice(1);
|
|
17163
|
+
return line2;
|
|
16325
17164
|
}
|
|
16326
|
-
function parseConstraintLine(
|
|
16327
|
-
const alterTable = extractAlterTableName(
|
|
16328
|
-
const createUniqueIndex =
|
|
17165
|
+
function parseConstraintLine(line2, trackedTable) {
|
|
17166
|
+
const alterTable = extractAlterTableName(line2) ?? trackedTable ?? "unknown";
|
|
17167
|
+
const createUniqueIndex = line2.match(new RegExp(String.raw`CREATE\s+UNIQUE\s+INDEX\s+(?:CONCURRENTLY\s+)?(?:(?:IF\s+NOT\s+EXISTS\s+)?(\w+)\s+)?ON\s+${TABLE_IDENT2}`, "i"));
|
|
16329
17168
|
if (createUniqueIndex) {
|
|
16330
17169
|
const constraintName = createUniqueIndex[1] ?? void 0;
|
|
16331
17170
|
const table = normaliseTable2(createUniqueIndex[2]);
|
|
16332
17171
|
return { table, kind: "unique", ...constraintName ? { constraintName } : {} };
|
|
16333
17172
|
}
|
|
16334
|
-
const addConstraint =
|
|
17173
|
+
const addConstraint = line2.match(/ADD\s+CONSTRAINT\s+(\w+)\s+(CHECK|UNIQUE|FOREIGN\s+KEY)\b/i);
|
|
16335
17174
|
if (addConstraint) {
|
|
16336
17175
|
const constraintName = addConstraint[1];
|
|
16337
17176
|
const kindRaw = addConstraint[2].toUpperCase();
|
|
@@ -16345,22 +17184,22 @@ function parseConstraintLine(line, trackedTable) {
|
|
|
16345
17184
|
}
|
|
16346
17185
|
return { table: alterTable, kind, constraintName };
|
|
16347
17186
|
}
|
|
16348
|
-
const addCheck =
|
|
17187
|
+
const addCheck = line2.match(/ADD\s+CHECK\s*\(/i);
|
|
16349
17188
|
if (addCheck) {
|
|
16350
17189
|
return { table: alterTable, kind: "check" };
|
|
16351
17190
|
}
|
|
16352
|
-
const addUnique =
|
|
17191
|
+
const addUnique = line2.match(/ADD\s+UNIQUE\s*\(/i);
|
|
16353
17192
|
if (addUnique) {
|
|
16354
17193
|
return { table: alterTable, kind: "unique" };
|
|
16355
17194
|
}
|
|
16356
|
-
const addForeignKey =
|
|
17195
|
+
const addForeignKey = line2.match(/ADD\s+FOREIGN\s+KEY\s*\(/i);
|
|
16357
17196
|
if (addForeignKey) {
|
|
16358
17197
|
return { table: alterTable, kind: "foreignKey" };
|
|
16359
17198
|
}
|
|
16360
17199
|
return null;
|
|
16361
17200
|
}
|
|
16362
|
-
function extractAlterTableName(
|
|
16363
|
-
const m =
|
|
17201
|
+
function extractAlterTableName(line2) {
|
|
17202
|
+
const m = line2.match(new RegExp(String.raw`ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?${TABLE_IDENT2}`, "i"));
|
|
16364
17203
|
return m ? normaliseTable2(m[1]) : void 0;
|
|
16365
17204
|
}
|
|
16366
17205
|
var TABLE_IDENT2 = String.raw`((?:"[^"]+"|[a-zA-Z_][a-zA-Z0-9_]*)(?:\.(?:"[^"]+"|[a-zA-Z_][a-zA-Z0-9_]*))?)`;
|
|
@@ -16381,10 +17220,10 @@ function detectBlastRadiusMarkers(filePath, diffContent) {
|
|
|
16381
17220
|
markerKind: "patch-every-field-path"
|
|
16382
17221
|
});
|
|
16383
17222
|
}
|
|
16384
|
-
for (const
|
|
16385
|
-
if (
|
|
17223
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17224
|
+
if (line2.startsWith("-") && !line2.startsWith("---"))
|
|
16386
17225
|
continue;
|
|
16387
|
-
const symbolMatch =
|
|
17226
|
+
const symbolMatch = line2.match(/\bUPDATABLE_FIELDS(?:_FOR_(\w+))?\b/);
|
|
16388
17227
|
if (symbolMatch) {
|
|
16389
17228
|
const table = symbolMatch[1] ? symbolMatch[1].toLowerCase() : "unknown";
|
|
16390
17229
|
markers.push({ table, markerKind: "updatable-fields-symbol" });
|
|
@@ -16398,10 +17237,10 @@ import { readFileSync as readFileSync2, readdirSync } from "node:fs";
|
|
|
16398
17237
|
import { join } from "node:path";
|
|
16399
17238
|
function extractNumericColumns(diffContent, context = {}) {
|
|
16400
17239
|
const addedLines = [];
|
|
16401
|
-
for (const
|
|
16402
|
-
if (
|
|
17240
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17241
|
+
if (line2.startsWith("-") && !line2.startsWith("---"))
|
|
16403
17242
|
continue;
|
|
16404
|
-
addedLines.push(stripDiffPrefix3(
|
|
17243
|
+
addedLines.push(stripDiffPrefix3(line2));
|
|
16405
17244
|
}
|
|
16406
17245
|
const columnDecls = extractColumnDeclarations(addedLines);
|
|
16407
17246
|
if (columnDecls.length === 0)
|
|
@@ -16480,19 +17319,19 @@ function walkForRegistrations(dir, repoRoot, acc) {
|
|
|
16480
17319
|
acc.add(m[1]);
|
|
16481
17320
|
}
|
|
16482
17321
|
}
|
|
16483
|
-
function stripDiffPrefix3(
|
|
16484
|
-
if (
|
|
16485
|
-
return
|
|
16486
|
-
if (
|
|
16487
|
-
return
|
|
16488
|
-
return
|
|
17322
|
+
function stripDiffPrefix3(line2) {
|
|
17323
|
+
if (line2.startsWith("+") && !line2.startsWith("+++"))
|
|
17324
|
+
return line2.slice(1);
|
|
17325
|
+
if (line2.startsWith(" "))
|
|
17326
|
+
return line2.slice(1);
|
|
17327
|
+
return line2;
|
|
16489
17328
|
}
|
|
16490
17329
|
function extractColumnDeclarations(lines) {
|
|
16491
17330
|
const decls = [];
|
|
16492
17331
|
let currentTable = null;
|
|
16493
17332
|
let insideCreateTable = false;
|
|
16494
|
-
for (const
|
|
16495
|
-
const trimmed =
|
|
17333
|
+
for (const line2 of lines) {
|
|
17334
|
+
const trimmed = line2.trim();
|
|
16496
17335
|
const alterAdd = trimmed.match(/ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(\w+)\s+ADD\s+(?:COLUMN\s+)?(?:IF\s+NOT\s+EXISTS\s+)?(\w+)\s+(NUMERIC|BIGINT|DECIMAL)(?:\s*\([^)]*\))?/i);
|
|
16497
17336
|
if (alterAdd) {
|
|
16498
17337
|
const table = alterAdd[1].toLowerCase();
|
|
@@ -16519,8 +17358,8 @@ function extractColumnDeclarations(lines) {
|
|
|
16519
17358
|
}
|
|
16520
17359
|
return decls;
|
|
16521
17360
|
}
|
|
16522
|
-
function checkInlineColumn(
|
|
16523
|
-
const colMatch =
|
|
17361
|
+
function checkInlineColumn(line2, table, decls) {
|
|
17362
|
+
const colMatch = line2.match(/^\s*(\w+)\s+(NUMERIC|BIGINT|DECIMAL)(?:\s*\([^)]*\))?(?:\s|,|$)/i);
|
|
16524
17363
|
if (colMatch) {
|
|
16525
17364
|
const name = colMatch[1].toUpperCase();
|
|
16526
17365
|
if (SQL_KEYWORDS.has(name))
|
|
@@ -16559,8 +17398,8 @@ function resolveWireFields(lines, decls) {
|
|
|
16559
17398
|
existing.push(decl);
|
|
16560
17399
|
colToDecls.set(decl.column, existing);
|
|
16561
17400
|
}
|
|
16562
|
-
for (const
|
|
16563
|
-
const trimmed =
|
|
17401
|
+
for (const line2 of lines) {
|
|
17402
|
+
const trimmed = line2.trim();
|
|
16564
17403
|
const assignment = trimmed.match(/^(\w+)\s*:\s*(?:\w+\.)?(\w+)\s*[,;]?\s*(?:\/\/.*)?$/);
|
|
16565
17404
|
if (assignment) {
|
|
16566
17405
|
const wireField = assignment[1];
|
|
@@ -16678,10 +17517,10 @@ function extractThirdPartyEndpoints(diffContent, context = {}) {
|
|
|
16678
17517
|
}
|
|
16679
17518
|
function detectEndpointMarkers(diffContent) {
|
|
16680
17519
|
const detected = [];
|
|
16681
|
-
for (const
|
|
16682
|
-
if (
|
|
17520
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17521
|
+
if (line2.startsWith("-") && !line2.startsWith("---"))
|
|
16683
17522
|
continue;
|
|
16684
|
-
const stripped = stripDiffPrefix4(
|
|
17523
|
+
const stripped = stripDiffPrefix4(line2).trim();
|
|
16685
17524
|
const decoratorSimple = stripped.match(/@ThirdParty(?:Endpoint)?\s*\(\s*['"]([^'"]+)['"]\s*\)/);
|
|
16686
17525
|
if (decoratorSimple) {
|
|
16687
17526
|
detected.push({ signature: decoratorSimple[1] });
|
|
@@ -16728,12 +17567,12 @@ function detectEndpointMarkers(diffContent) {
|
|
|
16728
17567
|
}
|
|
16729
17568
|
return detected;
|
|
16730
17569
|
}
|
|
16731
|
-
function stripDiffPrefix4(
|
|
16732
|
-
if (
|
|
16733
|
-
return
|
|
16734
|
-
if (
|
|
16735
|
-
return
|
|
16736
|
-
return
|
|
17570
|
+
function stripDiffPrefix4(line2) {
|
|
17571
|
+
if (line2.startsWith("+") && !line2.startsWith("+++"))
|
|
17572
|
+
return line2.slice(1);
|
|
17573
|
+
if (line2.startsWith(" "))
|
|
17574
|
+
return line2.slice(1);
|
|
17575
|
+
return line2;
|
|
16737
17576
|
}
|
|
16738
17577
|
|
|
16739
17578
|
// dist/changeset.js
|
|
@@ -17358,6 +18197,9 @@ function writeRefreshedToken(opts) {
|
|
|
17358
18197
|
// dist/credential-refresh.js
|
|
17359
18198
|
var CONTROL_TIMEOUT_MS = 5e3;
|
|
17360
18199
|
var CONNECTION_CHECK_DEADLINE_MS = 3e3;
|
|
18200
|
+
var SCOPE_NOTICE_DEADLINE_MS = 1500;
|
|
18201
|
+
var SESSION_START_HOOK_TIMEOUT_MS = 1e4;
|
|
18202
|
+
var SESSION_START_MARGIN_MS = 1500;
|
|
17361
18203
|
var UNAUTHORIZED = 401;
|
|
17362
18204
|
async function probeAccount(controlOrigin, credential, fetchFn = globalThis.fetch) {
|
|
17363
18205
|
const controller = new AbortController();
|
|
@@ -17649,7 +18491,7 @@ function readSeatConfiguration(repoRoot) {
|
|
|
17649
18491
|
paths: readPathStates(projectDir, repoRoot)
|
|
17650
18492
|
};
|
|
17651
18493
|
}
|
|
17652
|
-
function
|
|
18494
|
+
function isPlainObject3(value) {
|
|
17653
18495
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17654
18496
|
}
|
|
17655
18497
|
function readSettingsFile(projectDir, rel) {
|
|
@@ -17667,12 +18509,12 @@ function readSettingsFile(projectDir, rel) {
|
|
|
17667
18509
|
} catch {
|
|
17668
18510
|
return { path: rel, state: "unreadable", hooks: [] };
|
|
17669
18511
|
}
|
|
17670
|
-
if (!
|
|
18512
|
+
if (!isPlainObject3(parsed))
|
|
17671
18513
|
return { path: rel, state: "unreadable", hooks: [] };
|
|
17672
18514
|
return { path: rel, state: "read", hooks: ourHooks(parsed["hooks"]) };
|
|
17673
18515
|
}
|
|
17674
18516
|
function ourHooks(hooks) {
|
|
17675
|
-
if (!
|
|
18517
|
+
if (!isPlainObject3(hooks))
|
|
17676
18518
|
return [];
|
|
17677
18519
|
const rows = [];
|
|
17678
18520
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -17680,7 +18522,7 @@ function ourHooks(hooks) {
|
|
|
17680
18522
|
if (!Array.isArray(entries))
|
|
17681
18523
|
continue;
|
|
17682
18524
|
for (const entry of entries) {
|
|
17683
|
-
if (!
|
|
18525
|
+
if (!isPlainObject3(entry))
|
|
17684
18526
|
continue;
|
|
17685
18527
|
let matcher = null;
|
|
17686
18528
|
if (Object.hasOwn(entry, "matcher")) {
|
|
@@ -17692,7 +18534,7 @@ function ourHooks(hooks) {
|
|
|
17692
18534
|
if (!Array.isArray(commands))
|
|
17693
18535
|
continue;
|
|
17694
18536
|
for (const hook of commands) {
|
|
17695
|
-
if (!
|
|
18537
|
+
if (!isPlainObject3(hook))
|
|
17696
18538
|
continue;
|
|
17697
18539
|
const command = hook["command"];
|
|
17698
18540
|
if (typeof command !== "string" || !HALFCYCLE_HOOK_COMMANDS.has(command))
|
|
@@ -18138,18 +18980,233 @@ async function runStop() {
|
|
|
18138
18980
|
await runSessionDiff(input, "stop");
|
|
18139
18981
|
}
|
|
18140
18982
|
|
|
18141
|
-
// dist/
|
|
18142
|
-
import {
|
|
18143
|
-
import { join as join8, sep as sep2 } from "node:path";
|
|
18983
|
+
// dist/hooks/user-prompt-submit.js
|
|
18984
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
18144
18985
|
import { homedir as homedir4 } from "node:os";
|
|
18986
|
+
import { join as join8 } from "node:path";
|
|
18987
|
+
|
|
18988
|
+
// dist/hooks/scope-phrase.js
|
|
18989
|
+
var APPROVE_PREFIX = /^approve /i;
|
|
18990
|
+
var SEND_BACK_PREFIX = /^send back /i;
|
|
18991
|
+
var LINE_BREAK = /[\n\r\u000b\u000c\u0085\u2028\u2029]/;
|
|
18992
|
+
function scopePhraseShape(prompt) {
|
|
18993
|
+
if (typeof prompt !== "string")
|
|
18994
|
+
return null;
|
|
18995
|
+
const line2 = prompt.trim();
|
|
18996
|
+
if (LINE_BREAK.test(line2))
|
|
18997
|
+
return null;
|
|
18998
|
+
const approve = APPROVE_PREFIX.exec(line2);
|
|
18999
|
+
if (approve)
|
|
19000
|
+
return { act: "approve", rest: line2.slice(approve[0].length) };
|
|
19001
|
+
const sendBack = SEND_BACK_PREFIX.exec(line2);
|
|
19002
|
+
if (sendBack)
|
|
19003
|
+
return { act: "send-back", rest: line2.slice(sendBack[0].length) };
|
|
19004
|
+
return null;
|
|
19005
|
+
}
|
|
19006
|
+
function matchScopePhraseShape(shape, shown) {
|
|
19007
|
+
if (shape.act === "approve") {
|
|
19008
|
+
const entry = resolvePhaseReference(shape.rest, shown);
|
|
19009
|
+
return entry === null ? { kind: "show-first", act: "approve" } : { kind: "approve", entry, reference: shape.rest };
|
|
19010
|
+
}
|
|
19011
|
+
for (let colon = shape.rest.lastIndexOf(":"); colon > 0; colon = shape.rest.lastIndexOf(":", colon - 1)) {
|
|
19012
|
+
const reference = shape.rest.slice(0, colon);
|
|
19013
|
+
const entry = resolvePhaseReference(reference, shown);
|
|
19014
|
+
if (entry !== null) {
|
|
19015
|
+
return { kind: "send-back", entry, reference, note: shape.rest.slice(colon + 1).trim() };
|
|
19016
|
+
}
|
|
19017
|
+
}
|
|
19018
|
+
return { kind: "show-first", act: "send-back" };
|
|
19019
|
+
}
|
|
19020
|
+
|
|
19021
|
+
// dist/hooks/user-prompt-submit-lines.js
|
|
19022
|
+
var TELL_THE_PERSON = "Tell the person.";
|
|
19023
|
+
function line(text) {
|
|
19024
|
+
return `${text} ${TELL_THE_PERSON}`;
|
|
19025
|
+
}
|
|
19026
|
+
function nothingDone(act) {
|
|
19027
|
+
return act === "approve" ? "Nothing was approved" : "Nothing was sent back";
|
|
19028
|
+
}
|
|
19029
|
+
function showCommand(phase) {
|
|
19030
|
+
return `\`${HALFCYCLE_COMMAND} plan ${quoteForShell(phase)} --show\``;
|
|
19031
|
+
}
|
|
19032
|
+
function recordedLine(act, phase, planVersion) {
|
|
19033
|
+
return act === "approve" ? line(`Approved ${phase}, plan version ${planVersion}, in your session.`) : line(`Sent ${phase} back with your note, plan version ${planVersion}, in your session. The note is given first to the next session that asks what to do.`);
|
|
19034
|
+
}
|
|
19035
|
+
function notAttendedLine(act) {
|
|
19036
|
+
return line(`${nothingDone(act)}: approving or sending back by typing counts only when a person types it into an open session.`);
|
|
19037
|
+
}
|
|
19038
|
+
function notSignedInLine(act) {
|
|
19039
|
+
return line(`${nothingDone(act)}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
|
|
19040
|
+
}
|
|
19041
|
+
function showFirstLine(act) {
|
|
19042
|
+
return line(`${nothingDone(act)}: that phase has not been listed here. List its parts first with \`${HALFCYCLE_COMMAND} plan <phase> --show\`, then type the line again.`);
|
|
19043
|
+
}
|
|
19044
|
+
function tooOldLine(act, phase) {
|
|
19045
|
+
return line(`${nothingDone(act)}: ${phase} was listed an hour or more ago. List it again with ${showCommand(phase)}, then type the line again within the hour.`);
|
|
19046
|
+
}
|
|
19047
|
+
function noteNeededLine() {
|
|
19048
|
+
return line("Nothing was sent back: a send-back needs a note after the colon saying what should change.");
|
|
19049
|
+
}
|
|
19050
|
+
function noteLooksSecretLine() {
|
|
19051
|
+
return line("Nothing was sent back: the note looks like it holds a password or key, so it never left this computer. Type the send-back again without it.");
|
|
19052
|
+
}
|
|
19053
|
+
function noteTooLongLine() {
|
|
19054
|
+
return line("Nothing was sent back: the note is too long. Shorten it and type the send-back again.");
|
|
19055
|
+
}
|
|
19056
|
+
function noteRefusedLine() {
|
|
19057
|
+
return line("Nothing was sent back: the note was not accepted. Reword it in plain words and type the send-back again.");
|
|
19058
|
+
}
|
|
19059
|
+
function approveRefusedLine() {
|
|
19060
|
+
return line(`${nothingDone("approve")}: Halfcycle did not accept the request. ${CLIENT_UPDATE_SENTENCE}`);
|
|
19061
|
+
}
|
|
19062
|
+
function cannotReachLine(act) {
|
|
19063
|
+
return line(`${nothingDone(act)}: Halfcycle could not be reached just now. Type the line again in a minute.`);
|
|
19064
|
+
}
|
|
19065
|
+
function lineForAnswer(act, phase, answer) {
|
|
19066
|
+
switch (answer.outcome) {
|
|
19067
|
+
case "recorded":
|
|
19068
|
+
return recordedLine(answer.act, answer.phase, answer.planVersion);
|
|
19069
|
+
case "show-first":
|
|
19070
|
+
return showFirstLine(act);
|
|
19071
|
+
case "plan-changed":
|
|
19072
|
+
return line(`${nothingDone(act)}: the plan for ${phase} changed after it was listed here. List it again with ${showCommand(phase)}, check the parts, then type the line again.`);
|
|
19073
|
+
case "already-approved":
|
|
19074
|
+
return answer.replanNeeded ? line(`${phase} is already approved, so nothing changed. Some parts wait because the description of the system changed since the plan was written: write the plan for ${phase} again, run ${showCommand(phase)}, then approve the new version.`) : line(`${phase} is already approved, so nothing changed.`);
|
|
19075
|
+
case "build-started":
|
|
19076
|
+
return line(`${nothingDone(act)}: tasks in ${phase} have started, so its scope can no longer be sent back. A built part goes back at the phase's review.`);
|
|
19077
|
+
case "not-owner":
|
|
19078
|
+
return line(`${nothingDone(act)}: only the project's owner can approve or send back this scope.`);
|
|
19079
|
+
case "no-owner":
|
|
19080
|
+
return line(`${nothingDone(act)}: this project has no owner, so nobody can approve or send back its scope.`);
|
|
19081
|
+
case "not-offered":
|
|
19082
|
+
return line(`${nothingDone(act)}. ${TYPED_APPROVAL_OFF_SENTENCE}`);
|
|
19083
|
+
case "refused":
|
|
19084
|
+
return act === "approve" ? approveRefusedLine() : noteRefusedLine();
|
|
19085
|
+
case "unavailable":
|
|
19086
|
+
return cannotReachLine(act);
|
|
19087
|
+
default: {
|
|
19088
|
+
const unreachable = answer;
|
|
19089
|
+
return unreachable;
|
|
19090
|
+
}
|
|
19091
|
+
}
|
|
19092
|
+
}
|
|
19093
|
+
|
|
19094
|
+
// dist/hooks/user-prompt-submit.js
|
|
19095
|
+
var CHAT_REQUEST_TIMEOUT_MS = 6e3;
|
|
19096
|
+
var SHOWN_ENTRY_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
19097
|
+
async function runUserPromptSubmit(opts) {
|
|
19098
|
+
let act = null;
|
|
19099
|
+
try {
|
|
19100
|
+
const prompt = promptOf(opts.input);
|
|
19101
|
+
const shape = scopePhraseShape(prompt);
|
|
19102
|
+
if (shape === null)
|
|
19103
|
+
return "";
|
|
19104
|
+
act = shape.act;
|
|
19105
|
+
return `${await decide(shape, opts)}
|
|
19106
|
+
`;
|
|
19107
|
+
} catch {
|
|
19108
|
+
return act === null ? "" : `${cannotReachLine(act)}
|
|
19109
|
+
`;
|
|
19110
|
+
}
|
|
19111
|
+
}
|
|
19112
|
+
async function decide(shape, opts) {
|
|
19113
|
+
const { act } = shape;
|
|
19114
|
+
const env = opts.env ?? process.env;
|
|
19115
|
+
if (!readSessionAttended(env))
|
|
19116
|
+
return notAttendedLine(act);
|
|
19117
|
+
const read = readConfig(env);
|
|
19118
|
+
if (read.config === null)
|
|
19119
|
+
return notSignedInLine(act);
|
|
19120
|
+
const { guardServiceToken, guardEngagementId } = read.config;
|
|
19121
|
+
const shown = readShownPlans(guardEngagementId, opts.home);
|
|
19122
|
+
const match = matchScopePhraseShape(shape, shown);
|
|
19123
|
+
if (match.kind === "show-first")
|
|
19124
|
+
return showFirstLine(act);
|
|
19125
|
+
const { entry } = match;
|
|
19126
|
+
if (!shownRecently(entry, (opts.now ?? Date.now)()))
|
|
19127
|
+
return tooOldLine(act, entry.phase);
|
|
19128
|
+
let candidate;
|
|
19129
|
+
if (match.kind === "send-back") {
|
|
19130
|
+
if (match.note === "")
|
|
19131
|
+
return noteNeededLine();
|
|
19132
|
+
if (!scopeNoteWireSchema.safeParse(match.note).success)
|
|
19133
|
+
return noteTooLongLine();
|
|
19134
|
+
if (looksLikeSecret(match.note))
|
|
19135
|
+
return noteLooksSecretLine();
|
|
19136
|
+
candidate = { act: "send-back", reference: match.reference, planVersion: entry.version, note: match.note };
|
|
19137
|
+
} else {
|
|
19138
|
+
candidate = { act: "approve", reference: match.reference, planVersion: entry.version };
|
|
19139
|
+
}
|
|
19140
|
+
const request = chatScopeRequestSchema.safeParse(candidate);
|
|
19141
|
+
if (!request.success)
|
|
19142
|
+
return showFirstLine(act);
|
|
19143
|
+
return post(act, entry, request.data, guardEngagementId, guardServiceToken, env, opts.fetchFn);
|
|
19144
|
+
}
|
|
19145
|
+
function promptOf(input) {
|
|
19146
|
+
try {
|
|
19147
|
+
const parsed = JSON.parse(input);
|
|
19148
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
19149
|
+
return void 0;
|
|
19150
|
+
return parsed["prompt"];
|
|
19151
|
+
} catch {
|
|
19152
|
+
return void 0;
|
|
19153
|
+
}
|
|
19154
|
+
}
|
|
19155
|
+
function readShownPlans(engagementId, home) {
|
|
19156
|
+
if (!isEngagementId(engagementId))
|
|
19157
|
+
return [];
|
|
19158
|
+
let text;
|
|
19159
|
+
try {
|
|
19160
|
+
text = readFileSync8(join8(home ?? homedir4(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SHOWN_PLANS_FILENAME), "utf-8");
|
|
19161
|
+
} catch {
|
|
19162
|
+
return [];
|
|
19163
|
+
}
|
|
19164
|
+
return parseShownPlans(text);
|
|
19165
|
+
}
|
|
19166
|
+
function shownRecently(entry, now) {
|
|
19167
|
+
const age = now - Date.parse(entry.shownAt);
|
|
19168
|
+
return Number.isFinite(age) && age >= 0 && age < SHOWN_ENTRY_MAX_AGE_MS;
|
|
19169
|
+
}
|
|
19170
|
+
async function post(act, entry, request, engagementId, credential, env, fetchFn = globalThis.fetch) {
|
|
19171
|
+
const url2 = `${readControlOrigin(env).origin}/engagements/${encodeURIComponent(engagementId)}/phases/${encodeURIComponent(entry.phase)}/scope/chat`;
|
|
19172
|
+
const controller = new AbortController();
|
|
19173
|
+
const timer = setTimeout(() => controller.abort(), CHAT_REQUEST_TIMEOUT_MS);
|
|
19174
|
+
try {
|
|
19175
|
+
const response = await fetchFn(url2, {
|
|
19176
|
+
method: "POST",
|
|
19177
|
+
headers: { Authorization: `Bearer ${credential}`, "Content-Type": "application/json" },
|
|
19178
|
+
body: JSON.stringify(request),
|
|
19179
|
+
signal: controller.signal
|
|
19180
|
+
});
|
|
19181
|
+
if (response.status === 401)
|
|
19182
|
+
return notSignedInLine(act);
|
|
19183
|
+
if (response.status === 429 || response.status >= 500)
|
|
19184
|
+
return cannotReachLine(act);
|
|
19185
|
+
const parsed = chatScopeResultSchema.safeParse(await response.json());
|
|
19186
|
+
if (!parsed.success)
|
|
19187
|
+
return cannotReachLine(act);
|
|
19188
|
+
if (parsed.data.outcome === "recorded" && response.status !== 200)
|
|
19189
|
+
return cannotReachLine(act);
|
|
19190
|
+
return lineForAnswer(act, entry.phase, parsed.data);
|
|
19191
|
+
} catch {
|
|
19192
|
+
return cannotReachLine(act);
|
|
19193
|
+
} finally {
|
|
19194
|
+
clearTimeout(timer);
|
|
19195
|
+
}
|
|
19196
|
+
}
|
|
19197
|
+
|
|
19198
|
+
// dist/resolve-engagement.js
|
|
19199
|
+
import { existsSync as existsSync6, readFileSync as readFileSync9, realpathSync as realpathSync3 } from "node:fs";
|
|
19200
|
+
import { join as join9, sep as sep2 } from "node:path";
|
|
19201
|
+
import { homedir as homedir5 } from "node:os";
|
|
18145
19202
|
function defaultRegistryPath() {
|
|
18146
|
-
return
|
|
19203
|
+
return join9(homedir5(), ".halfcycle", "registry.json");
|
|
18147
19204
|
}
|
|
18148
19205
|
function readRegistry(registryPath = defaultRegistryPath()) {
|
|
18149
19206
|
if (!existsSync6(registryPath))
|
|
18150
19207
|
return {};
|
|
18151
19208
|
try {
|
|
18152
|
-
const parsed = JSON.parse(
|
|
19209
|
+
const parsed = JSON.parse(readFileSync9(registryPath, "utf-8"));
|
|
18153
19210
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
18154
19211
|
return {};
|
|
18155
19212
|
const out = {};
|
|
@@ -18197,18 +19254,21 @@ function runResolveEngagement(cwd, registryPath) {
|
|
|
18197
19254
|
}
|
|
18198
19255
|
|
|
18199
19256
|
// dist/refresh-credential.js
|
|
18200
|
-
import { readFileSync as
|
|
19257
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
18201
19258
|
import { resolve as resolve3 } from "node:path";
|
|
18202
19259
|
|
|
18203
19260
|
// dist/mcp-headers.js
|
|
18204
|
-
import { readFileSync as
|
|
18205
|
-
import { homedir as
|
|
19261
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
19262
|
+
import { homedir as homedir6 } from "node:os";
|
|
18206
19263
|
import { dirname as dirname4, resolve as resolve2 } from "node:path";
|
|
18207
19264
|
function defaultWriteErr(text) {
|
|
18208
19265
|
process.stderr.write(text);
|
|
18209
19266
|
}
|
|
18210
|
-
function headerLine(bearer) {
|
|
18211
|
-
|
|
19267
|
+
function headerLine(bearer, clientVersion) {
|
|
19268
|
+
const header = { Authorization: `Bearer ${bearer}` };
|
|
19269
|
+
if (clientVersion !== void 0)
|
|
19270
|
+
header[CLIENT_VERSION_HEADER] = clientVersion;
|
|
19271
|
+
return `${JSON.stringify(header)}
|
|
18212
19272
|
`;
|
|
18213
19273
|
}
|
|
18214
19274
|
function projectRootFromBinary(binaryPath) {
|
|
@@ -18218,7 +19278,7 @@ function projectRootFromBinary(binaryPath) {
|
|
|
18218
19278
|
function readPinnedId(root) {
|
|
18219
19279
|
let raw;
|
|
18220
19280
|
try {
|
|
18221
|
-
raw =
|
|
19281
|
+
raw = readFileSync10(resolve2(root, BUNDLE_PIN_REL), "utf-8");
|
|
18222
19282
|
} catch {
|
|
18223
19283
|
return { problem: "no-pin" };
|
|
18224
19284
|
}
|
|
@@ -18231,6 +19291,22 @@ function readPinnedId(root) {
|
|
|
18231
19291
|
const id = typeof pin === "object" && pin !== null ? pin[PIN_ENGAGEMENT_ID_FIELD] : void 0;
|
|
18232
19292
|
return typeof id === "string" && id !== "" ? { id } : { problem: "no-id" };
|
|
18233
19293
|
}
|
|
19294
|
+
function readPinnedVersion(root) {
|
|
19295
|
+
let raw;
|
|
19296
|
+
try {
|
|
19297
|
+
raw = readFileSync10(resolve2(root, BUNDLE_PIN_REL), "utf-8");
|
|
19298
|
+
} catch {
|
|
19299
|
+
return void 0;
|
|
19300
|
+
}
|
|
19301
|
+
let pin;
|
|
19302
|
+
try {
|
|
19303
|
+
pin = JSON.parse(raw);
|
|
19304
|
+
} catch {
|
|
19305
|
+
return void 0;
|
|
19306
|
+
}
|
|
19307
|
+
const version2 = typeof pin === "object" && pin !== null ? pin["version"] : void 0;
|
|
19308
|
+
return typeof version2 === "string" && version2 !== "" ? version2 : void 0;
|
|
19309
|
+
}
|
|
18234
19310
|
function resolveStore(root, home) {
|
|
18235
19311
|
const pinned = readPinnedId(root);
|
|
18236
19312
|
if ("problem" in pinned)
|
|
@@ -18255,7 +19331,7 @@ function shownUrl(url2) {
|
|
|
18255
19331
|
return url2.replace(/[^\x21-\x7e]/g, "").slice(0, 200);
|
|
18256
19332
|
}
|
|
18257
19333
|
function sentences(lines) {
|
|
18258
|
-
return lines.map((
|
|
19334
|
+
return lines.map((line2) => `halfcycle: ${line2}
|
|
18259
19335
|
`).join("");
|
|
18260
19336
|
}
|
|
18261
19337
|
function badIdSentence(root) {
|
|
@@ -18312,7 +19388,7 @@ async function refreshWithinDeadline(source, opts, writeErr) {
|
|
|
18312
19388
|
}
|
|
18313
19389
|
async function chooseBearer(opts) {
|
|
18314
19390
|
const writeErr = opts.writeErr ?? defaultWriteErr;
|
|
18315
|
-
const home = opts.home ??
|
|
19391
|
+
const home = opts.home ?? homedir6();
|
|
18316
19392
|
const root = projectRootFromBinary(opts.binaryPath);
|
|
18317
19393
|
const environmentToken = opts.environmentTokenFirst ? readEnvironmentToken() : void 0;
|
|
18318
19394
|
const store = resolveStore(root, home);
|
|
@@ -18376,6 +19452,7 @@ async function chooseBearer(opts) {
|
|
|
18376
19452
|
}
|
|
18377
19453
|
async function mcpHeaders(opts) {
|
|
18378
19454
|
const writeErr = opts.writeErr ?? defaultWriteErr;
|
|
19455
|
+
const root = projectRootFromBinary(opts.binaryPath);
|
|
18379
19456
|
try {
|
|
18380
19457
|
return headerLine(await chooseBearer({
|
|
18381
19458
|
...opts,
|
|
@@ -18383,7 +19460,7 @@ async function mcpHeaders(opts) {
|
|
|
18383
19460
|
requestedUrl: readMcpServerUrl(),
|
|
18384
19461
|
renewal: "renew-here",
|
|
18385
19462
|
environmentTokenFirst: true
|
|
18386
|
-
}));
|
|
19463
|
+
}), readPinnedVersion(root));
|
|
18387
19464
|
} catch (err) {
|
|
18388
19465
|
try {
|
|
18389
19466
|
writeErr(sentences([
|
|
@@ -18391,9 +19468,9 @@ async function mcpHeaders(opts) {
|
|
|
18391
19468
|
`run "npx halfcycle" in this repository to rewrite this machine's credential.`
|
|
18392
19469
|
]));
|
|
18393
19470
|
} catch {
|
|
18394
|
-
return headerLine(NO_CREDENTIAL_PLACEHOLDER);
|
|
19471
|
+
return headerLine(NO_CREDENTIAL_PLACEHOLDER, readPinnedVersion(root));
|
|
18395
19472
|
}
|
|
18396
|
-
return headerLine(NO_CREDENTIAL_PLACEHOLDER);
|
|
19473
|
+
return headerLine(NO_CREDENTIAL_PLACEHOLDER, readPinnedVersion(root));
|
|
18397
19474
|
}
|
|
18398
19475
|
}
|
|
18399
19476
|
|
|
@@ -18441,7 +19518,7 @@ var INITIALIZE_BODY = JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initializ
|
|
|
18441
19518
|
function registeredMcpUrl(projectRoot) {
|
|
18442
19519
|
let parsed;
|
|
18443
19520
|
try {
|
|
18444
|
-
parsed = JSON.parse(
|
|
19521
|
+
parsed = JSON.parse(readFileSync11(resolve3(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
|
|
18445
19522
|
} catch {
|
|
18446
19523
|
return void 0;
|
|
18447
19524
|
}
|
|
@@ -18535,16 +19612,83 @@ async function checkConnection(renewedToken, opts) {
|
|
|
18535
19612
|
environmentTokenFirst: false
|
|
18536
19613
|
});
|
|
18537
19614
|
if (CREDENTIAL_PLACEHOLDERS.includes(bearer)) {
|
|
18538
|
-
return PLACEHOLDER_SENTENCES[bearer];
|
|
19615
|
+
return { sentence: PLACEHOLDER_SENTENCES[bearer], url: url2, bearer };
|
|
18539
19616
|
}
|
|
18540
19617
|
const outcome = await probeDelivery(url2, bearer, opts.fetchFn ?? globalThis.fetch, opts.checkDeadlineMs ?? CONNECTION_CHECK_DEADLINE_MS);
|
|
18541
|
-
return outcome ?? CONNECTED_SENTENCE;
|
|
19618
|
+
return { sentence: outcome ?? CONNECTED_SENTENCE, url: url2, bearer };
|
|
19619
|
+
}
|
|
19620
|
+
function positionRequestBody(engagementId) {
|
|
19621
|
+
return JSON.stringify({
|
|
19622
|
+
jsonrpc: "2.0",
|
|
19623
|
+
id: 1,
|
|
19624
|
+
method: "tools/call",
|
|
19625
|
+
params: { name: "halfcycle_position", arguments: { engagementId } }
|
|
19626
|
+
});
|
|
19627
|
+
}
|
|
19628
|
+
async function fetchScopeNotices(url2, bearer, engagementId, fetchFn, deadlineMs) {
|
|
19629
|
+
const controller = new AbortController();
|
|
19630
|
+
const timer = setTimeout(() => controller.abort(), deadlineMs);
|
|
19631
|
+
try {
|
|
19632
|
+
let res;
|
|
19633
|
+
try {
|
|
19634
|
+
res = await fetchFn(url2, {
|
|
19635
|
+
method: "POST",
|
|
19636
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
|
|
19637
|
+
body: positionRequestBody(engagementId),
|
|
19638
|
+
redirect: "manual",
|
|
19639
|
+
signal: controller.signal
|
|
19640
|
+
});
|
|
19641
|
+
} catch {
|
|
19642
|
+
return void 0;
|
|
19643
|
+
}
|
|
19644
|
+
if (res.status < 200 || res.status > 299) {
|
|
19645
|
+
discardBody(res);
|
|
19646
|
+
return void 0;
|
|
19647
|
+
}
|
|
19648
|
+
let text;
|
|
19649
|
+
try {
|
|
19650
|
+
text = await res.text();
|
|
19651
|
+
} catch {
|
|
19652
|
+
return void 0;
|
|
19653
|
+
}
|
|
19654
|
+
let rpc;
|
|
19655
|
+
try {
|
|
19656
|
+
rpc = JSON.parse(text);
|
|
19657
|
+
} catch {
|
|
19658
|
+
return void 0;
|
|
19659
|
+
}
|
|
19660
|
+
const result = typeof rpc === "object" && rpc !== null ? rpc["result"] : void 0;
|
|
19661
|
+
if (typeof result !== "object" || result === null || result["isError"] === true) {
|
|
19662
|
+
return void 0;
|
|
19663
|
+
}
|
|
19664
|
+
const content = result["content"];
|
|
19665
|
+
const first = Array.isArray(content) ? content[0] : void 0;
|
|
19666
|
+
const bodyText = typeof first === "object" && first !== null ? first["text"] : void 0;
|
|
19667
|
+
if (typeof bodyText !== "string")
|
|
19668
|
+
return void 0;
|
|
19669
|
+
let body;
|
|
19670
|
+
try {
|
|
19671
|
+
body = JSON.parse(bodyText);
|
|
19672
|
+
} catch {
|
|
19673
|
+
return void 0;
|
|
19674
|
+
}
|
|
19675
|
+
const scope = typeof body === "object" && body !== null ? body["scope"] : void 0;
|
|
19676
|
+
const parsed = scopeNoticesSchema.safeParse(scope);
|
|
19677
|
+
return parsed.success ? parsed.data : void 0;
|
|
19678
|
+
} finally {
|
|
19679
|
+
clearTimeout(timer);
|
|
19680
|
+
}
|
|
19681
|
+
}
|
|
19682
|
+
function scopeNoticeLines(scope) {
|
|
19683
|
+
const text = scopeNoticeText(scope, { offerTyping: scope.typedApprovalOffered ? "yes" : "off" });
|
|
19684
|
+
return [...text.sentBack, ...text.waiting];
|
|
18542
19685
|
}
|
|
18543
19686
|
function defaultWriteErr2(text) {
|
|
18544
19687
|
process.stderr.write(text);
|
|
18545
19688
|
}
|
|
18546
19689
|
async function runRefreshCredential(opts) {
|
|
18547
19690
|
const writeErr = opts.writeErr ?? defaultWriteErr2;
|
|
19691
|
+
const startedAt = Date.now();
|
|
18548
19692
|
try {
|
|
18549
19693
|
const renewalSaid = [];
|
|
18550
19694
|
let renewalOpen = true;
|
|
@@ -18558,9 +19702,24 @@ async function runRefreshCredential(opts) {
|
|
|
18558
19702
|
renewalOpen = false;
|
|
18559
19703
|
const parts = [];
|
|
18560
19704
|
if (CHECKED_SOURCES.has(input["source"])) {
|
|
18561
|
-
const
|
|
18562
|
-
if (
|
|
18563
|
-
parts.push(
|
|
19705
|
+
const connection = await checkConnection(renewedToken, { ...opts, writeErr });
|
|
19706
|
+
if (connection !== void 0) {
|
|
19707
|
+
parts.push(connection.sentence);
|
|
19708
|
+
if (connection.sentence === CONNECTED_SENTENCE) {
|
|
19709
|
+
const engagementId = readConfig().config?.guardEngagementId;
|
|
19710
|
+
if (engagementId !== void 0) {
|
|
19711
|
+
const remainingForMargin = SESSION_START_HOOK_TIMEOUT_MS - SESSION_START_MARGIN_MS - (Date.now() - startedAt);
|
|
19712
|
+
const positionDeadline = Math.min(opts.scopeNoticeDeadlineMs ?? SCOPE_NOTICE_DEADLINE_MS, remainingForMargin);
|
|
19713
|
+
if (positionDeadline > 0) {
|
|
19714
|
+
const scope = await fetchScopeNotices(connection.url, connection.bearer, engagementId, opts.fetchFn ?? globalThis.fetch, positionDeadline);
|
|
19715
|
+
if (scope !== void 0)
|
|
19716
|
+
parts.push(...scopeNoticeLines(scope));
|
|
19717
|
+
}
|
|
19718
|
+
}
|
|
19719
|
+
} else if (connection.sentence === CANNOT_REACH_SENTENCE || connection.sentence === SERVICE_PROBLEM_SENTENCE) {
|
|
19720
|
+
parts.push(SCOPE_UNCHECKED_SENTENCE);
|
|
19721
|
+
}
|
|
19722
|
+
}
|
|
18564
19723
|
}
|
|
18565
19724
|
if (readConfig().config === null) {
|
|
18566
19725
|
const remedySaid = parts.some(namesTheSignInCommand);
|
|
@@ -18588,7 +19747,7 @@ async function runRefreshCredential(opts) {
|
|
|
18588
19747
|
import { fileURLToPath } from "node:url";
|
|
18589
19748
|
|
|
18590
19749
|
// dist/capture.js
|
|
18591
|
-
import { readFileSync as
|
|
19750
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync7 } from "node:fs";
|
|
18592
19751
|
import { dirname as dirname5 } from "node:path";
|
|
18593
19752
|
function runCapture(provider, endpoint, opts = {}) {
|
|
18594
19753
|
const repoRoot = opts.repoRoot ?? process.cwd();
|
|
@@ -18656,7 +19815,7 @@ function readInputFile(fromFile) {
|
|
|
18656
19815
|
if (!fromFile)
|
|
18657
19816
|
return void 0;
|
|
18658
19817
|
try {
|
|
18659
|
-
return
|
|
19818
|
+
return readFileSync12(fromFile, "utf-8");
|
|
18660
19819
|
} catch {
|
|
18661
19820
|
return void 0;
|
|
18662
19821
|
}
|
|
@@ -18681,8 +19840,8 @@ async function main() {
|
|
|
18681
19840
|
return;
|
|
18682
19841
|
}
|
|
18683
19842
|
if (cmd === "mcp-headers") {
|
|
18684
|
-
const
|
|
18685
|
-
process.stdout.write(
|
|
19843
|
+
const line2 = await mcpHeaders({ binaryPath: fileURLToPath(import.meta.url) });
|
|
19844
|
+
process.stdout.write(line2, () => process.exit(0));
|
|
18686
19845
|
return;
|
|
18687
19846
|
}
|
|
18688
19847
|
if (cmd === "resolve-engagement") {
|
|
@@ -18738,8 +19897,18 @@ Commit the fixture; the next guard evaluation of this endpoint will pass.
|
|
|
18738
19897
|
process.exit(process.exitCode ?? 0);
|
|
18739
19898
|
return;
|
|
18740
19899
|
}
|
|
19900
|
+
if (sub === "user-prompt-submit") {
|
|
19901
|
+
let reply = "";
|
|
19902
|
+
try {
|
|
19903
|
+
reply = await runUserPromptSubmit({ input: await readStdin2() });
|
|
19904
|
+
} catch {
|
|
19905
|
+
reply = "";
|
|
19906
|
+
}
|
|
19907
|
+
process.stdout.write(reply, () => process.exit(0));
|
|
19908
|
+
return;
|
|
19909
|
+
}
|
|
18741
19910
|
process.stderr.write(`halfcycle-runner: unknown hook "${sub ?? ""}"
|
|
18742
|
-
Valid hooks: post-tool-use, stop, subagent-stop
|
|
19911
|
+
Valid hooks: post-tool-use, stop, subagent-stop, user-prompt-submit
|
|
18743
19912
|
`);
|
|
18744
19913
|
process.exit(1);
|
|
18745
19914
|
return;
|
|
@@ -18749,6 +19918,7 @@ Commit the fixture; the next guard evaluation of this endpoint will pass.
|
|
|
18749
19918
|
halfcycle-runner hook post-tool-use
|
|
18750
19919
|
halfcycle-runner hook stop
|
|
18751
19920
|
halfcycle-runner hook subagent-stop
|
|
19921
|
+
halfcycle-runner hook user-prompt-submit
|
|
18752
19922
|
halfcycle-runner refresh-credential
|
|
18753
19923
|
halfcycle-runner mcp-headers
|
|
18754
19924
|
halfcycle-runner resolve-engagement <cwd>
|