halfcycle 0.4.6 → 0.4.8
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 +1492 -200
- 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 +2 -1
- package/dist/bin.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 +1470 -423
- 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 +76 -32
- package/dist/draw/index.d.ts.map +1 -1
- package/dist/draw/write.d.ts +71 -21
- package/dist/draw/write.d.ts.map +1 -1
- package/dist/index.js +74 -11
- package/dist/index.js.map +3 -3
- package/dist/install.d.ts +33 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/loopback-signin.d.ts.map +1 -1
- 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 +20 -9
- package/dist/system-report.d.ts.map +1 -1
- 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) {
|
|
@@ -14799,8 +14899,8 @@ function readConfig(env = process.env) {
|
|
|
14799
14899
|
}
|
|
14800
14900
|
};
|
|
14801
14901
|
}
|
|
14802
|
-
function readNeverConfiguredReason() {
|
|
14803
|
-
return
|
|
14902
|
+
function readNeverConfiguredReason(env = process.env) {
|
|
14903
|
+
return env["HALFCYCLE_NOT_THIS_ACCOUNT"] === "1" ? "not-this-account" : "no-credential-yet";
|
|
14804
14904
|
}
|
|
14805
14905
|
function isHalfcycleOwnCi() {
|
|
14806
14906
|
return process.env["HALFCYCLE_INTERNAL_CI"] === "1";
|
|
@@ -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,787 @@ 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
|
+
|
|
16089
|
+
// ../events/dist/device-auth.js
|
|
16090
|
+
var utcIso86012 = 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");
|
|
16091
|
+
var DEVICE_AUTH_STATUS = {
|
|
16092
|
+
/** The poll: minted, nobody has acted yet. Keep polling. 200. */
|
|
16093
|
+
PENDING: "pending",
|
|
16094
|
+
/** The poll: approved, and the credential is in this body. Once only. 200. */
|
|
16095
|
+
APPROVED: "approved",
|
|
16096
|
+
/** Confirm: the decision was recorded (approve or decline). 200. */
|
|
16097
|
+
RECORDED: "recorded",
|
|
16098
|
+
/** The human refused at the confirmation page. 400 on the poll. */
|
|
16099
|
+
DECLINED: "declined",
|
|
16100
|
+
/** The code's absolute expiry has passed. 400. */
|
|
16101
|
+
EXPIRED: "expired",
|
|
16102
|
+
/** Never minted, or already spent. 400. */
|
|
16103
|
+
UNKNOWN: "unknown",
|
|
16104
|
+
/** Confirm: neither `approve` nor `decline` was named. Nothing was bound. 400. */
|
|
16105
|
+
INVALID_DECISION: "invalid-decision",
|
|
16106
|
+
/** Confirm: an approval arrived with no signed-in identity. 400. */
|
|
16107
|
+
IDENTITY_REQUIRED: "identity-required",
|
|
16108
|
+
/** This deployment has no `SIGNIN_BASE_URL` / `DEVICE_AUTH_CONFIRM_SECRET`. 503. */
|
|
16109
|
+
NOT_CONFIGURED: "not-configured",
|
|
16110
|
+
/** Confirm: the shared confirmation secret was missing or wrong. 401. */
|
|
16111
|
+
UNAUTHORIZED: "unauthorized",
|
|
16112
|
+
/** Confirm: the code was already approved, declined or claimed. 409. */
|
|
16113
|
+
ALREADY_DECIDED: "already-decided",
|
|
16114
|
+
/**
|
|
16115
|
+
* Confirm: an approval named a signed-in identity whose account has not accepted
|
|
16116
|
+
* the terms of service and privacy policy currently in force. 400.
|
|
16117
|
+
*
|
|
16118
|
+
* NOT BOUND TO THE DEVICE CODE. Unlike every other refusal in this const, this one
|
|
16119
|
+
* says nothing about the code itself — the code is still `pending` after this
|
|
16120
|
+
* response, exactly as it was before the request, so the SAME userCode can be
|
|
16121
|
+
* confirmed again once the caller has recorded acceptance. `deviceConfirmRequestSchema`'s
|
|
16122
|
+
* `acceptTerms` is how a second confirm says it did.
|
|
16123
|
+
*/
|
|
16124
|
+
TERMS_REQUIRED: "terms-required"
|
|
16125
|
+
};
|
|
16126
|
+
var DEVICE_POLL_REFUSALS = [
|
|
16127
|
+
DEVICE_AUTH_STATUS.DECLINED,
|
|
16128
|
+
DEVICE_AUTH_STATUS.EXPIRED,
|
|
16129
|
+
DEVICE_AUTH_STATUS.UNKNOWN
|
|
16130
|
+
];
|
|
16131
|
+
var deviceAuthStartResponseSchema = external_exports.object({
|
|
16132
|
+
deviceCode: external_exports.string().min(1),
|
|
16133
|
+
userCode: external_exports.string().min(1),
|
|
16134
|
+
verificationUrl: external_exports.string().min(1),
|
|
16135
|
+
expiresAt: utcIso86012,
|
|
16136
|
+
pollIntervalMs: external_exports.number().int().positive()
|
|
16137
|
+
}).strict();
|
|
16138
|
+
var devicePollPendingSchema = external_exports.object({
|
|
16139
|
+
status: external_exports.literal(DEVICE_AUTH_STATUS.PENDING),
|
|
16140
|
+
pollIntervalMs: external_exports.number().int().positive()
|
|
16141
|
+
}).strict();
|
|
16142
|
+
var devicePollApprovedSchema = external_exports.object({
|
|
16143
|
+
status: external_exports.literal(DEVICE_AUTH_STATUS.APPROVED),
|
|
16144
|
+
accountId: external_exports.string().min(1),
|
|
16145
|
+
credential: external_exports.string().min(1),
|
|
16146
|
+
expiresAt: utcIso86012
|
|
16147
|
+
}).strict();
|
|
16148
|
+
var deviceAuthRefusalSchema = external_exports.object({
|
|
16149
|
+
status: external_exports.enum([
|
|
16150
|
+
DEVICE_AUTH_STATUS.DECLINED,
|
|
16151
|
+
DEVICE_AUTH_STATUS.EXPIRED,
|
|
16152
|
+
DEVICE_AUTH_STATUS.UNKNOWN,
|
|
16153
|
+
DEVICE_AUTH_STATUS.INVALID_DECISION,
|
|
16154
|
+
DEVICE_AUTH_STATUS.IDENTITY_REQUIRED,
|
|
16155
|
+
DEVICE_AUTH_STATUS.NOT_CONFIGURED,
|
|
16156
|
+
DEVICE_AUTH_STATUS.UNAUTHORIZED,
|
|
16157
|
+
DEVICE_AUTH_STATUS.ALREADY_DECIDED,
|
|
16158
|
+
DEVICE_AUTH_STATUS.TERMS_REQUIRED
|
|
16159
|
+
]),
|
|
16160
|
+
message: external_exports.string().min(1)
|
|
16161
|
+
}).strict();
|
|
16162
|
+
var devicePollResponseSchema = external_exports.union([
|
|
16163
|
+
devicePollPendingSchema,
|
|
16164
|
+
devicePollApprovedSchema,
|
|
16165
|
+
deviceAuthRefusalSchema
|
|
16166
|
+
]);
|
|
16167
|
+
var deviceConfirmRequestSchema = external_exports.object({
|
|
16168
|
+
userCode: external_exports.string().min(1),
|
|
16169
|
+
decision: external_exports.enum(["approve", "decline"]),
|
|
16170
|
+
externalAuthId: external_exports.string().min(1).optional(),
|
|
16171
|
+
email: external_exports.string().optional(),
|
|
16172
|
+
acceptTerms: external_exports.boolean().optional()
|
|
16173
|
+
}).strict();
|
|
16174
|
+
var deviceConfirmRecordedSchema = external_exports.object({
|
|
16175
|
+
status: external_exports.literal(DEVICE_AUTH_STATUS.RECORDED),
|
|
16176
|
+
accountId: external_exports.string().min(1).optional()
|
|
16177
|
+
}).strict();
|
|
16178
|
+
var deviceConfirmResponseSchema = external_exports.union([
|
|
16179
|
+
deviceConfirmRecordedSchema,
|
|
16180
|
+
deviceAuthRefusalSchema
|
|
16181
|
+
]);
|
|
16182
|
+
var DASHBOARD_URL = "https://app.halfcycle.ai";
|
|
16183
|
+
|
|
16184
|
+
// ../events/dist/project-page.js
|
|
16185
|
+
function projectPageUrl(engagementId) {
|
|
16186
|
+
return isEngagementId(engagementId) ? `${DASHBOARD_URL}/${encodeURIComponent(engagementId)}` : void 0;
|
|
16187
|
+
}
|
|
16188
|
+
function requestAccessSentence(engagementId) {
|
|
16189
|
+
const page = projectPageUrl(engagementId) ?? "this project's page on Halfcycle";
|
|
16190
|
+
return `To ask for access, open ${page} signed in as this account and choose "Request access". Then let the project's owner know, so they can approve it.`;
|
|
16191
|
+
}
|
|
16192
|
+
|
|
15298
16193
|
// ../events/dist/credential-wire.js
|
|
15299
16194
|
var engagementCredentialResponseSchema = external_exports.object({
|
|
15300
16195
|
engagementId: external_exports.string(),
|
|
@@ -15324,12 +16219,60 @@ var NOT_THIS_ACCOUNT_SENTENCE = `Halfcycle is not connected: this project belong
|
|
|
15324
16219
|
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
16220
|
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
16221
|
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.";
|
|
16222
|
+
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
16223
|
var PLACEHOLDER_SENTENCES = Object.freeze({
|
|
15328
16224
|
[NO_CREDENTIAL_PLACEHOLDER]: NO_CREDENTIAL_SENTENCE,
|
|
15329
16225
|
[NOT_THIS_ACCOUNT_PLACEHOLDER]: NOT_THIS_ACCOUNT_SENTENCE,
|
|
15330
16226
|
[UPDATE_CLAUDE_CODE_PLACEHOLDER]: UPDATE_CLAUDE_CODE_SENTENCE
|
|
15331
16227
|
});
|
|
15332
16228
|
|
|
16229
|
+
// ../events/dist/scope-chat.js
|
|
16230
|
+
var chatScopeApproveRequestSchema = external_exports.object({
|
|
16231
|
+
act: external_exports.literal("approve"),
|
|
16232
|
+
reference: phaseIdentitySchema,
|
|
16233
|
+
planVersion: planVersionSchema
|
|
16234
|
+
}).strict();
|
|
16235
|
+
var chatScopeSendBackRequestSchema = external_exports.object({
|
|
16236
|
+
act: external_exports.literal("send-back"),
|
|
16237
|
+
reference: phaseIdentitySchema,
|
|
16238
|
+
planVersion: planVersionSchema,
|
|
16239
|
+
note: scopeNoteWireSchema.optional()
|
|
16240
|
+
}).strict();
|
|
16241
|
+
var chatScopeRequestSchema = external_exports.discriminatedUnion("act", [
|
|
16242
|
+
chatScopeApproveRequestSchema,
|
|
16243
|
+
chatScopeSendBackRequestSchema
|
|
16244
|
+
]);
|
|
16245
|
+
var CHAT_SCOPE_ACTS = ["approve", "send-back"];
|
|
16246
|
+
var chatScopeRecordedSchema = external_exports.object({
|
|
16247
|
+
outcome: external_exports.literal("recorded"),
|
|
16248
|
+
act: external_exports.enum(CHAT_SCOPE_ACTS),
|
|
16249
|
+
phase: scopePhaseSchema,
|
|
16250
|
+
planVersion: planVersionSchema
|
|
16251
|
+
}).strict();
|
|
16252
|
+
var chatScopeAlreadyApprovedSchema = external_exports.object({
|
|
16253
|
+
outcome: external_exports.literal("already-approved"),
|
|
16254
|
+
replanNeeded: external_exports.boolean()
|
|
16255
|
+
}).strict();
|
|
16256
|
+
var chatScopeRefusedSchema = external_exports.object({
|
|
16257
|
+
outcome: external_exports.literal("refused"),
|
|
16258
|
+
problems: external_exports.array(systemDescriptionProblemSchema).min(1)
|
|
16259
|
+
}).strict();
|
|
16260
|
+
function bareOutcome(outcome) {
|
|
16261
|
+
return external_exports.object({ outcome: external_exports.literal(outcome) }).strict();
|
|
16262
|
+
}
|
|
16263
|
+
var chatScopeResultSchema = external_exports.discriminatedUnion("outcome", [
|
|
16264
|
+
chatScopeRecordedSchema,
|
|
16265
|
+
chatScopeAlreadyApprovedSchema,
|
|
16266
|
+
chatScopeRefusedSchema,
|
|
16267
|
+
bareOutcome("show-first"),
|
|
16268
|
+
bareOutcome("plan-changed"),
|
|
16269
|
+
bareOutcome("build-started"),
|
|
16270
|
+
bareOutcome("not-owner"),
|
|
16271
|
+
bareOutcome("no-owner"),
|
|
16272
|
+
bareOutcome("not-offered"),
|
|
16273
|
+
bareOutcome("unavailable")
|
|
16274
|
+
]);
|
|
16275
|
+
|
|
15333
16276
|
// dist/hook-protocol.js
|
|
15334
16277
|
import { writeSync } from "node:fs";
|
|
15335
16278
|
function emitBlock(reason) {
|
|
@@ -15385,8 +16328,8 @@ This is not a service outage: the guard service answered. The suspect is this re
|
|
|
15385
16328
|
}
|
|
15386
16329
|
var UNCONFIGURED_EXIT_TOOL_USE = 2;
|
|
15387
16330
|
var UNCONFIGURED_EXIT_STOP_FAMILY = 1;
|
|
15388
|
-
function emitNeverConfigured(exitCode, reason) {
|
|
15389
|
-
writeSync(2, neverConfiguredReport(reason));
|
|
16331
|
+
function emitNeverConfigured(exitCode, reason, engagementId) {
|
|
16332
|
+
writeSync(2, neverConfiguredReport(reason, void 0, engagementId));
|
|
15390
16333
|
process.exitCode = exitCode;
|
|
15391
16334
|
}
|
|
15392
16335
|
var SIGN_IN_COMMAND = '"npx halfcycle"';
|
|
@@ -15416,16 +16359,16 @@ ${NOT_A_SERVICE_OUTAGE} Nothing was sent: this job holds no credential and was n
|
|
|
15416
16359
|
${CI_OLDER_SETUP}
|
|
15417
16360
|
`;
|
|
15418
16361
|
}
|
|
15419
|
-
function notThisAccountReport(label) {
|
|
15420
|
-
return `${label} This project belongs to another Halfcycle account. Halfcycle is Solo today \u2014 one person works on a project at a time \u2014 so no credential exists for this machine and none will be issued. Nothing here needs fixing; this copy simply is not checked.
|
|
16362
|
+
function notThisAccountReport(label, engagementId) {
|
|
16363
|
+
return `${label} This project belongs to another Halfcycle account. Halfcycle is Solo today \u2014 one person works on a project at a time \u2014 so no credential exists for this machine and none will be issued. Nothing here needs fixing; this copy simply is not checked. ${requestAccessSentence(engagementId)} If this account is wrong, sign in as the one that owns the project and run "npx halfcycle" again.
|
|
15421
16364
|
`;
|
|
15422
16365
|
}
|
|
15423
|
-
function neverConfiguredReport(reason, label = "[Halfcycle]") {
|
|
16366
|
+
function neverConfiguredReport(reason, label = "[Halfcycle]", engagementId) {
|
|
15424
16367
|
switch (reason) {
|
|
15425
16368
|
case "no-credential-yet":
|
|
15426
16369
|
return neverConfiguredMessage(HOOK_REMEDY, label);
|
|
15427
16370
|
case "not-this-account":
|
|
15428
|
-
return notThisAccountReport(label);
|
|
16371
|
+
return notThisAccountReport(label, engagementId);
|
|
15429
16372
|
}
|
|
15430
16373
|
}
|
|
15431
16374
|
var CREDENTIAL_REJECTED_STATUSES = /* @__PURE__ */ new Set([401, 403]);
|
|
@@ -15476,14 +16419,14 @@ function credentialRenewalFailedMessage(remedy, label = "[Halfcycle]") {
|
|
|
15476
16419
|
${remedy}
|
|
15477
16420
|
`;
|
|
15478
16421
|
}
|
|
15479
|
-
function noCoverageStatement(reason, remedySaid = false) {
|
|
16422
|
+
function noCoverageStatement(reason, remedySaid = false, engagementId) {
|
|
15480
16423
|
if (reason === "not-this-account") {
|
|
15481
16424
|
return [
|
|
15482
16425
|
"[halfcycle] This project belongs to another Halfcycle account, so nothing here is checked.",
|
|
15483
16426
|
"[halfcycle] Your work carries on exactly as it would without Halfcycle \u2014 no edit is",
|
|
15484
16427
|
"[halfcycle] evaluated, and nothing you do is reported as having passed.",
|
|
15485
16428
|
"[halfcycle] Halfcycle is Solo today \u2014 one developer per project \u2014 with Team plans coming.",
|
|
15486
|
-
|
|
16429
|
+
`[halfcycle] ${requestAccessSentence(engagementId)}`,
|
|
15487
16430
|
"[halfcycle] If this account is wrong, sign in as the one that owns the project and run",
|
|
15488
16431
|
'[halfcycle] "npx halfcycle" again.'
|
|
15489
16432
|
].join("\n");
|
|
@@ -15915,16 +16858,16 @@ function detectEnvReads(diffContent, jsComments) {
|
|
|
15915
16858
|
if (raw.startsWith("-") && !raw.startsWith("---"))
|
|
15916
16859
|
continue;
|
|
15917
16860
|
const isSource = raw.startsWith("+") && !raw.startsWith("+++") || raw.startsWith(" ");
|
|
15918
|
-
const
|
|
15919
|
-
const comment = jsComments && isSource ? commentedOut(
|
|
16861
|
+
const line2 = jsComments && isSource ? raw.slice(1) : raw;
|
|
16862
|
+
const comment = jsComments && isSource ? commentedOut(line2) : void 0;
|
|
15920
16863
|
const live = (match) => comment === void 0 || !comment(match.index, match.index + match[0].length);
|
|
15921
|
-
for (const match of
|
|
16864
|
+
for (const match of line2.matchAll(dotPattern))
|
|
15922
16865
|
if (live(match))
|
|
15923
16866
|
found.add(match[1]);
|
|
15924
|
-
for (const match of
|
|
16867
|
+
for (const match of line2.matchAll(bracketPattern))
|
|
15925
16868
|
if (live(match))
|
|
15926
16869
|
found.add(match[1]);
|
|
15927
|
-
for (const destructure of
|
|
16870
|
+
for (const destructure of line2.matchAll(destructurePattern)) {
|
|
15928
16871
|
if (!live(destructure))
|
|
15929
16872
|
continue;
|
|
15930
16873
|
for (const part of destructure[1].split(",")) {
|
|
@@ -15937,25 +16880,25 @@ function detectEnvReads(diffContent, jsComments) {
|
|
|
15937
16880
|
return found;
|
|
15938
16881
|
}
|
|
15939
16882
|
var LINE_TERMINATOR = /[\r\u2028\u2029]/g;
|
|
15940
|
-
function commentedOut(
|
|
15941
|
-
const lead =
|
|
16883
|
+
function commentedOut(line2) {
|
|
16884
|
+
const lead = line2.length - line2.trimStart().length;
|
|
15942
16885
|
let body;
|
|
15943
16886
|
let close;
|
|
15944
16887
|
let expression;
|
|
15945
|
-
if (
|
|
16888
|
+
if (line2.startsWith("//", lead)) {
|
|
15946
16889
|
body = lead + 2;
|
|
15947
16890
|
LINE_TERMINATOR.lastIndex = body;
|
|
15948
|
-
close = LINE_TERMINATOR.exec(
|
|
16891
|
+
close = LINE_TERMINATOR.exec(line2)?.index ?? line2.length;
|
|
15949
16892
|
expression = "{";
|
|
15950
|
-
} else if (
|
|
15951
|
-
body = lead + (
|
|
15952
|
-
const found =
|
|
15953
|
-
close = found === -1 ?
|
|
15954
|
-
expression =
|
|
16893
|
+
} else if (line2.startsWith("/*", lead) || line2.startsWith("{/*", lead)) {
|
|
16894
|
+
body = lead + (line2[lead] === "{" ? 3 : 2);
|
|
16895
|
+
const found = line2.indexOf("*/", body);
|
|
16896
|
+
close = found === -1 ? line2.length : found;
|
|
16897
|
+
expression = line2[lead] === "{" ? "${" : "{";
|
|
15955
16898
|
} else {
|
|
15956
16899
|
return void 0;
|
|
15957
16900
|
}
|
|
15958
|
-
const opened =
|
|
16901
|
+
const opened = line2.indexOf(expression, body);
|
|
15959
16902
|
return (start, end) => start >= body && end <= close && (opened === -1 || opened >= start || opened >= close);
|
|
15960
16903
|
}
|
|
15961
16904
|
function collectDeclarationFiles(context) {
|
|
@@ -16034,8 +16977,8 @@ function flyTomlSectionHas(name, content, sectionHeader) {
|
|
|
16034
16977
|
const lines = content.split("\n");
|
|
16035
16978
|
let inSection = false;
|
|
16036
16979
|
const keyPattern = new RegExp(`^${name}\\s*=`);
|
|
16037
|
-
for (const
|
|
16038
|
-
const trimmed =
|
|
16980
|
+
for (const line2 of lines) {
|
|
16981
|
+
const trimmed = line2.trim();
|
|
16039
16982
|
if (trimmed === sectionHeader) {
|
|
16040
16983
|
inSection = true;
|
|
16041
16984
|
continue;
|
|
@@ -16086,10 +17029,10 @@ function extractSqlWrites(diffContent) {
|
|
|
16086
17029
|
seen.add(key);
|
|
16087
17030
|
writes.push(write);
|
|
16088
17031
|
};
|
|
16089
|
-
for (const
|
|
16090
|
-
if (isRemovedLine(
|
|
17032
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17033
|
+
if (isRemovedLine(line2) || isContextLine(line2) || isDiffHeader(line2))
|
|
16091
17034
|
continue;
|
|
16092
|
-
const code = stripComments(stripDiffPrefix(
|
|
17035
|
+
const code = stripComments(stripDiffPrefix(line2));
|
|
16093
17036
|
push(parseRawSql(code));
|
|
16094
17037
|
push(parseQueryBuilder(code));
|
|
16095
17038
|
push(parseOrmCall(code));
|
|
@@ -16099,23 +17042,23 @@ function extractSqlWrites(diffContent) {
|
|
|
16099
17042
|
}
|
|
16100
17043
|
return writes;
|
|
16101
17044
|
}
|
|
16102
|
-
function isRemovedLine(
|
|
16103
|
-
return
|
|
17045
|
+
function isRemovedLine(line2) {
|
|
17046
|
+
return line2.startsWith("-") && !line2.startsWith("---");
|
|
16104
17047
|
}
|
|
16105
|
-
function isContextLine(
|
|
16106
|
-
return
|
|
17048
|
+
function isContextLine(line2) {
|
|
17049
|
+
return line2.startsWith(" ");
|
|
16107
17050
|
}
|
|
16108
|
-
function isDiffHeader(
|
|
16109
|
-
return
|
|
17051
|
+
function isDiffHeader(line2) {
|
|
17052
|
+
return line2.startsWith("+++") || line2.startsWith("---");
|
|
16110
17053
|
}
|
|
16111
|
-
function stripDiffPrefix(
|
|
16112
|
-
if (
|
|
16113
|
-
return
|
|
16114
|
-
if (
|
|
16115
|
-
return
|
|
16116
|
-
if (
|
|
16117
|
-
return
|
|
16118
|
-
return
|
|
17054
|
+
function stripDiffPrefix(line2) {
|
|
17055
|
+
if (line2.startsWith("+") && !line2.startsWith("+++"))
|
|
17056
|
+
return line2.slice(1);
|
|
17057
|
+
if (line2.startsWith("-") && !line2.startsWith("---"))
|
|
17058
|
+
return line2.slice(1);
|
|
17059
|
+
if (line2.startsWith(" "))
|
|
17060
|
+
return line2.slice(1);
|
|
17061
|
+
return line2;
|
|
16119
17062
|
}
|
|
16120
17063
|
function assembleStatements(diffContent) {
|
|
16121
17064
|
const statements = [];
|
|
@@ -16128,16 +17071,16 @@ function assembleStatements(diffContent) {
|
|
|
16128
17071
|
buffer = [];
|
|
16129
17072
|
hasAddedLine = false;
|
|
16130
17073
|
};
|
|
16131
|
-
for (const
|
|
16132
|
-
if (
|
|
17074
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17075
|
+
if (line2.startsWith("@@") || isDiffHeader(line2)) {
|
|
16133
17076
|
flush(true);
|
|
16134
17077
|
continue;
|
|
16135
17078
|
}
|
|
16136
|
-
if (isRemovedLine(
|
|
17079
|
+
if (isRemovedLine(line2)) {
|
|
16137
17080
|
flush(false);
|
|
16138
17081
|
continue;
|
|
16139
17082
|
}
|
|
16140
|
-
const rawContent = stripDiffPrefix(
|
|
17083
|
+
const rawContent = stripDiffPrefix(line2);
|
|
16141
17084
|
if (rawContent.trim() === "") {
|
|
16142
17085
|
flush(true);
|
|
16143
17086
|
continue;
|
|
@@ -16145,7 +17088,7 @@ function assembleStatements(diffContent) {
|
|
|
16145
17088
|
const content = stripComments(rawContent);
|
|
16146
17089
|
if (content.trim() === "")
|
|
16147
17090
|
continue;
|
|
16148
|
-
if (
|
|
17091
|
+
if (line2.startsWith("+"))
|
|
16149
17092
|
hasAddedLine = true;
|
|
16150
17093
|
buffer.push(content.trim());
|
|
16151
17094
|
if (content.includes(";"))
|
|
@@ -16162,44 +17105,44 @@ function normaliseTable(raw) {
|
|
|
16162
17105
|
return name.toLowerCase();
|
|
16163
17106
|
}
|
|
16164
17107
|
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 =
|
|
17108
|
+
function parseRawSql(line2) {
|
|
17109
|
+
if (/\bON\s+CONFLICT\b/i.test(line2)) {
|
|
17110
|
+
const m = line2.match(new RegExp(String.raw`INSERT\s+INTO\s+${TABLE_IDENT}`, "i"));
|
|
16168
17111
|
if (m)
|
|
16169
17112
|
return { kind: "upsert", table: normaliseTable(m[1]), whereColumns: [] };
|
|
16170
17113
|
}
|
|
16171
|
-
const upsertM =
|
|
17114
|
+
const upsertM = line2.match(new RegExp(String.raw`UPSERT\s+INTO\s+${TABLE_IDENT}`, "i"));
|
|
16172
17115
|
if (upsertM)
|
|
16173
17116
|
return { kind: "upsert", table: normaliseTable(upsertM[1]), whereColumns: [] };
|
|
16174
|
-
const insertM =
|
|
17117
|
+
const insertM = line2.match(new RegExp(String.raw`INSERT\s+INTO\s+${TABLE_IDENT}`, "i"));
|
|
16175
17118
|
if (insertM)
|
|
16176
17119
|
return { kind: "insert", table: normaliseTable(insertM[1]), whereColumns: [] };
|
|
16177
|
-
const updateM =
|
|
17120
|
+
const updateM = line2.match(new RegExp(String.raw`\bUPDATE\s+${TABLE_IDENT}\s+[\s\S]*?\bSET\b`, "i"));
|
|
16178
17121
|
if (updateM) {
|
|
16179
17122
|
return {
|
|
16180
17123
|
kind: "update",
|
|
16181
17124
|
table: normaliseTable(updateM[1]),
|
|
16182
|
-
whereColumns: extractWhereColumns(
|
|
17125
|
+
whereColumns: extractWhereColumns(line2)
|
|
16183
17126
|
};
|
|
16184
17127
|
}
|
|
16185
|
-
const deleteM =
|
|
17128
|
+
const deleteM = line2.match(new RegExp(String.raw`DELETE\s+FROM\s+${TABLE_IDENT}`, "i"));
|
|
16186
17129
|
if (deleteM) {
|
|
16187
17130
|
return {
|
|
16188
17131
|
kind: "delete",
|
|
16189
17132
|
table: normaliseTable(deleteM[1]),
|
|
16190
|
-
whereColumns: extractWhereColumns(
|
|
17133
|
+
whereColumns: extractWhereColumns(line2)
|
|
16191
17134
|
};
|
|
16192
17135
|
}
|
|
16193
17136
|
return null;
|
|
16194
17137
|
}
|
|
16195
17138
|
function parseQueryBuilder(rawLine) {
|
|
16196
|
-
const
|
|
16197
|
-
const m =
|
|
17139
|
+
const line2 = stripDiffPrefix(rawLine);
|
|
17140
|
+
const m = line2.match(/(?<!\bcreate(?:Hash|Hmac|Sign|Verify|Cipheriv|Decipheriv)\s*\([^)]*)['"]([\w]+)['"]\s*\)\s*\.(insert|update|upsert|delete)\s*\(/i);
|
|
16198
17141
|
if (m) {
|
|
16199
17142
|
const kind = m[2].toLowerCase();
|
|
16200
17143
|
return { kind, table: m[1].toLowerCase(), whereColumns: [] };
|
|
16201
17144
|
}
|
|
16202
|
-
const fromM =
|
|
17145
|
+
const fromM = line2.match(/\.(?:from|into|table)\s*\(\s*['"]([\w]+)['"]\s*\)\s*\.(insert|update|upsert|delete)\s*\(/i);
|
|
16203
17146
|
if (fromM) {
|
|
16204
17147
|
const kind = fromM[2].toLowerCase();
|
|
16205
17148
|
return { kind, table: fromM[1].toLowerCase(), whereColumns: [] };
|
|
@@ -16223,11 +17166,11 @@ var TYPEORM_KIND = {
|
|
|
16223
17166
|
delete: "delete"
|
|
16224
17167
|
};
|
|
16225
17168
|
function parseOrmCall(rawLine) {
|
|
16226
|
-
const
|
|
16227
|
-
return parsePrisma(
|
|
17169
|
+
const line2 = stripDiffPrefix(rawLine);
|
|
17170
|
+
return parsePrisma(line2) ?? parseTypeorm(line2) ?? parseDrizzle(line2);
|
|
16228
17171
|
}
|
|
16229
|
-
function parsePrisma(
|
|
16230
|
-
const m =
|
|
17172
|
+
function parsePrisma(line2) {
|
|
17173
|
+
const m = line2.match(/\b[\w$]+\.([a-zA-Z_$][\w$]*)\.(create|createMany|update|updateMany|upsert|delete|deleteMany)\s*\(/);
|
|
16231
17174
|
if (!m)
|
|
16232
17175
|
return null;
|
|
16233
17176
|
const kind = PRISMA_KIND[m[2].toLowerCase()];
|
|
@@ -16235,29 +17178,29 @@ function parsePrisma(line) {
|
|
|
16235
17178
|
return null;
|
|
16236
17179
|
return { kind, table: m[1].toLowerCase(), whereColumns: [] };
|
|
16237
17180
|
}
|
|
16238
|
-
function parseDrizzle(
|
|
16239
|
-
const upd =
|
|
17181
|
+
function parseDrizzle(line2) {
|
|
17182
|
+
const upd = line2.match(/\b[\w$]+\.update\s*\(\s*['"]?([\w$]+)['"]?\s*\)\s*\.set\s*\(/);
|
|
16240
17183
|
if (upd)
|
|
16241
17184
|
return { kind: "update", table: upd[1].toLowerCase(), whereColumns: [] };
|
|
16242
|
-
const ins =
|
|
17185
|
+
const ins = line2.match(/\b[\w$]+\.insert\s*\(\s*['"]?([\w$]+)['"]?\s*\)/);
|
|
16243
17186
|
if (ins)
|
|
16244
17187
|
return { kind: "insert", table: ins[1].toLowerCase(), whereColumns: [] };
|
|
16245
|
-
const del =
|
|
17188
|
+
const del = line2.match(/\b[\w$]+\.delete\s*\(\s*['"]?([\w$]+)['"]?\s*\)/);
|
|
16246
17189
|
if (del)
|
|
16247
17190
|
return { kind: "delete", table: del[1].toLowerCase(), whereColumns: [] };
|
|
16248
17191
|
return null;
|
|
16249
17192
|
}
|
|
16250
|
-
function parseTypeorm(
|
|
16251
|
-
const qb =
|
|
17193
|
+
function parseTypeorm(line2) {
|
|
17194
|
+
const qb = line2.match(/\.createQueryBuilder\s*\([^)]*\)[\s\S]*?\.update\s*\(\s*([a-zA-Z_$][\w$]*)/);
|
|
16252
17195
|
if (qb)
|
|
16253
17196
|
return { kind: "update", table: qb[1].toLowerCase(), whereColumns: [] };
|
|
16254
|
-
const saveUpsert =
|
|
17197
|
+
const saveUpsert = line2.match(/(?:^|[^.\w$])([a-zA-Z_$][\w$]*)\.(save|upsert)\s*\(/);
|
|
16255
17198
|
if (saveUpsert) {
|
|
16256
17199
|
const kind = TYPEORM_KIND[saveUpsert[2].toLowerCase()];
|
|
16257
17200
|
if (kind)
|
|
16258
17201
|
return { kind, table: saveUpsert[1].toLowerCase(), whereColumns: [] };
|
|
16259
17202
|
}
|
|
16260
|
-
const repo =
|
|
17203
|
+
const repo = line2.match(/(?:^|[^.\w$])([a-zA-Z_$][\w$]*(?:repo|repository))\.(insert|update|delete)\s*\(/i);
|
|
16261
17204
|
if (repo) {
|
|
16262
17205
|
const kind = TYPEORM_KIND[repo[2].toLowerCase()];
|
|
16263
17206
|
if (kind)
|
|
@@ -16265,8 +17208,8 @@ function parseTypeorm(line) {
|
|
|
16265
17208
|
}
|
|
16266
17209
|
return null;
|
|
16267
17210
|
}
|
|
16268
|
-
function extractWhereColumns(
|
|
16269
|
-
const whereM =
|
|
17211
|
+
function extractWhereColumns(line2) {
|
|
17212
|
+
const whereM = line2.match(/\bWHERE\s+(.+)/i);
|
|
16270
17213
|
if (!whereM)
|
|
16271
17214
|
return [];
|
|
16272
17215
|
const fragment = whereM[1].replace(/\s+(?:ORDER|LIMIT|GROUP|HAVING)\b.*/i, "");
|
|
@@ -16282,17 +17225,17 @@ function extractSchemaConstraints(diffContent) {
|
|
|
16282
17225
|
const results = [];
|
|
16283
17226
|
const seen = /* @__PURE__ */ new Set();
|
|
16284
17227
|
let currentTable;
|
|
16285
|
-
for (const
|
|
16286
|
-
if (
|
|
17228
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17229
|
+
if (line2.startsWith("-") && !line2.startsWith("---")) {
|
|
16287
17230
|
currentTable = void 0;
|
|
16288
17231
|
continue;
|
|
16289
17232
|
}
|
|
16290
|
-
if (
|
|
17233
|
+
if (line2.startsWith("@@")) {
|
|
16291
17234
|
currentTable = void 0;
|
|
16292
17235
|
continue;
|
|
16293
17236
|
}
|
|
16294
|
-
const isAdded =
|
|
16295
|
-
const raw = stripDiffPrefix2(
|
|
17237
|
+
const isAdded = line2.startsWith("+") && !line2.startsWith("+++");
|
|
17238
|
+
const raw = stripDiffPrefix2(line2);
|
|
16296
17239
|
if (raw.trim() === "") {
|
|
16297
17240
|
currentTable = void 0;
|
|
16298
17241
|
continue;
|
|
@@ -16316,22 +17259,22 @@ function extractSchemaConstraints(diffContent) {
|
|
|
16316
17259
|
}
|
|
16317
17260
|
return results;
|
|
16318
17261
|
}
|
|
16319
|
-
function stripDiffPrefix2(
|
|
16320
|
-
if (
|
|
16321
|
-
return
|
|
16322
|
-
if (
|
|
16323
|
-
return
|
|
16324
|
-
return
|
|
17262
|
+
function stripDiffPrefix2(line2) {
|
|
17263
|
+
if (line2.startsWith("+") && !line2.startsWith("+++"))
|
|
17264
|
+
return line2.slice(1);
|
|
17265
|
+
if (line2.startsWith(" "))
|
|
17266
|
+
return line2.slice(1);
|
|
17267
|
+
return line2;
|
|
16325
17268
|
}
|
|
16326
|
-
function parseConstraintLine(
|
|
16327
|
-
const alterTable = extractAlterTableName(
|
|
16328
|
-
const createUniqueIndex =
|
|
17269
|
+
function parseConstraintLine(line2, trackedTable) {
|
|
17270
|
+
const alterTable = extractAlterTableName(line2) ?? trackedTable ?? "unknown";
|
|
17271
|
+
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
17272
|
if (createUniqueIndex) {
|
|
16330
17273
|
const constraintName = createUniqueIndex[1] ?? void 0;
|
|
16331
17274
|
const table = normaliseTable2(createUniqueIndex[2]);
|
|
16332
17275
|
return { table, kind: "unique", ...constraintName ? { constraintName } : {} };
|
|
16333
17276
|
}
|
|
16334
|
-
const addConstraint =
|
|
17277
|
+
const addConstraint = line2.match(/ADD\s+CONSTRAINT\s+(\w+)\s+(CHECK|UNIQUE|FOREIGN\s+KEY)\b/i);
|
|
16335
17278
|
if (addConstraint) {
|
|
16336
17279
|
const constraintName = addConstraint[1];
|
|
16337
17280
|
const kindRaw = addConstraint[2].toUpperCase();
|
|
@@ -16345,22 +17288,22 @@ function parseConstraintLine(line, trackedTable) {
|
|
|
16345
17288
|
}
|
|
16346
17289
|
return { table: alterTable, kind, constraintName };
|
|
16347
17290
|
}
|
|
16348
|
-
const addCheck =
|
|
17291
|
+
const addCheck = line2.match(/ADD\s+CHECK\s*\(/i);
|
|
16349
17292
|
if (addCheck) {
|
|
16350
17293
|
return { table: alterTable, kind: "check" };
|
|
16351
17294
|
}
|
|
16352
|
-
const addUnique =
|
|
17295
|
+
const addUnique = line2.match(/ADD\s+UNIQUE\s*\(/i);
|
|
16353
17296
|
if (addUnique) {
|
|
16354
17297
|
return { table: alterTable, kind: "unique" };
|
|
16355
17298
|
}
|
|
16356
|
-
const addForeignKey =
|
|
17299
|
+
const addForeignKey = line2.match(/ADD\s+FOREIGN\s+KEY\s*\(/i);
|
|
16357
17300
|
if (addForeignKey) {
|
|
16358
17301
|
return { table: alterTable, kind: "foreignKey" };
|
|
16359
17302
|
}
|
|
16360
17303
|
return null;
|
|
16361
17304
|
}
|
|
16362
|
-
function extractAlterTableName(
|
|
16363
|
-
const m =
|
|
17305
|
+
function extractAlterTableName(line2) {
|
|
17306
|
+
const m = line2.match(new RegExp(String.raw`ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?${TABLE_IDENT2}`, "i"));
|
|
16364
17307
|
return m ? normaliseTable2(m[1]) : void 0;
|
|
16365
17308
|
}
|
|
16366
17309
|
var TABLE_IDENT2 = String.raw`((?:"[^"]+"|[a-zA-Z_][a-zA-Z0-9_]*)(?:\.(?:"[^"]+"|[a-zA-Z_][a-zA-Z0-9_]*))?)`;
|
|
@@ -16381,10 +17324,10 @@ function detectBlastRadiusMarkers(filePath, diffContent) {
|
|
|
16381
17324
|
markerKind: "patch-every-field-path"
|
|
16382
17325
|
});
|
|
16383
17326
|
}
|
|
16384
|
-
for (const
|
|
16385
|
-
if (
|
|
17327
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17328
|
+
if (line2.startsWith("-") && !line2.startsWith("---"))
|
|
16386
17329
|
continue;
|
|
16387
|
-
const symbolMatch =
|
|
17330
|
+
const symbolMatch = line2.match(/\bUPDATABLE_FIELDS(?:_FOR_(\w+))?\b/);
|
|
16388
17331
|
if (symbolMatch) {
|
|
16389
17332
|
const table = symbolMatch[1] ? symbolMatch[1].toLowerCase() : "unknown";
|
|
16390
17333
|
markers.push({ table, markerKind: "updatable-fields-symbol" });
|
|
@@ -16398,10 +17341,10 @@ import { readFileSync as readFileSync2, readdirSync } from "node:fs";
|
|
|
16398
17341
|
import { join } from "node:path";
|
|
16399
17342
|
function extractNumericColumns(diffContent, context = {}) {
|
|
16400
17343
|
const addedLines = [];
|
|
16401
|
-
for (const
|
|
16402
|
-
if (
|
|
17344
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17345
|
+
if (line2.startsWith("-") && !line2.startsWith("---"))
|
|
16403
17346
|
continue;
|
|
16404
|
-
addedLines.push(stripDiffPrefix3(
|
|
17347
|
+
addedLines.push(stripDiffPrefix3(line2));
|
|
16405
17348
|
}
|
|
16406
17349
|
const columnDecls = extractColumnDeclarations(addedLines);
|
|
16407
17350
|
if (columnDecls.length === 0)
|
|
@@ -16480,19 +17423,19 @@ function walkForRegistrations(dir, repoRoot, acc) {
|
|
|
16480
17423
|
acc.add(m[1]);
|
|
16481
17424
|
}
|
|
16482
17425
|
}
|
|
16483
|
-
function stripDiffPrefix3(
|
|
16484
|
-
if (
|
|
16485
|
-
return
|
|
16486
|
-
if (
|
|
16487
|
-
return
|
|
16488
|
-
return
|
|
17426
|
+
function stripDiffPrefix3(line2) {
|
|
17427
|
+
if (line2.startsWith("+") && !line2.startsWith("+++"))
|
|
17428
|
+
return line2.slice(1);
|
|
17429
|
+
if (line2.startsWith(" "))
|
|
17430
|
+
return line2.slice(1);
|
|
17431
|
+
return line2;
|
|
16489
17432
|
}
|
|
16490
17433
|
function extractColumnDeclarations(lines) {
|
|
16491
17434
|
const decls = [];
|
|
16492
17435
|
let currentTable = null;
|
|
16493
17436
|
let insideCreateTable = false;
|
|
16494
|
-
for (const
|
|
16495
|
-
const trimmed =
|
|
17437
|
+
for (const line2 of lines) {
|
|
17438
|
+
const trimmed = line2.trim();
|
|
16496
17439
|
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
17440
|
if (alterAdd) {
|
|
16498
17441
|
const table = alterAdd[1].toLowerCase();
|
|
@@ -16519,8 +17462,8 @@ function extractColumnDeclarations(lines) {
|
|
|
16519
17462
|
}
|
|
16520
17463
|
return decls;
|
|
16521
17464
|
}
|
|
16522
|
-
function checkInlineColumn(
|
|
16523
|
-
const colMatch =
|
|
17465
|
+
function checkInlineColumn(line2, table, decls) {
|
|
17466
|
+
const colMatch = line2.match(/^\s*(\w+)\s+(NUMERIC|BIGINT|DECIMAL)(?:\s*\([^)]*\))?(?:\s|,|$)/i);
|
|
16524
17467
|
if (colMatch) {
|
|
16525
17468
|
const name = colMatch[1].toUpperCase();
|
|
16526
17469
|
if (SQL_KEYWORDS.has(name))
|
|
@@ -16559,8 +17502,8 @@ function resolveWireFields(lines, decls) {
|
|
|
16559
17502
|
existing.push(decl);
|
|
16560
17503
|
colToDecls.set(decl.column, existing);
|
|
16561
17504
|
}
|
|
16562
|
-
for (const
|
|
16563
|
-
const trimmed =
|
|
17505
|
+
for (const line2 of lines) {
|
|
17506
|
+
const trimmed = line2.trim();
|
|
16564
17507
|
const assignment = trimmed.match(/^(\w+)\s*:\s*(?:\w+\.)?(\w+)\s*[,;]?\s*(?:\/\/.*)?$/);
|
|
16565
17508
|
if (assignment) {
|
|
16566
17509
|
const wireField = assignment[1];
|
|
@@ -16678,10 +17621,10 @@ function extractThirdPartyEndpoints(diffContent, context = {}) {
|
|
|
16678
17621
|
}
|
|
16679
17622
|
function detectEndpointMarkers(diffContent) {
|
|
16680
17623
|
const detected = [];
|
|
16681
|
-
for (const
|
|
16682
|
-
if (
|
|
17624
|
+
for (const line2 of diffContent.split("\n")) {
|
|
17625
|
+
if (line2.startsWith("-") && !line2.startsWith("---"))
|
|
16683
17626
|
continue;
|
|
16684
|
-
const stripped = stripDiffPrefix4(
|
|
17627
|
+
const stripped = stripDiffPrefix4(line2).trim();
|
|
16685
17628
|
const decoratorSimple = stripped.match(/@ThirdParty(?:Endpoint)?\s*\(\s*['"]([^'"]+)['"]\s*\)/);
|
|
16686
17629
|
if (decoratorSimple) {
|
|
16687
17630
|
detected.push({ signature: decoratorSimple[1] });
|
|
@@ -16728,12 +17671,12 @@ function detectEndpointMarkers(diffContent) {
|
|
|
16728
17671
|
}
|
|
16729
17672
|
return detected;
|
|
16730
17673
|
}
|
|
16731
|
-
function stripDiffPrefix4(
|
|
16732
|
-
if (
|
|
16733
|
-
return
|
|
16734
|
-
if (
|
|
16735
|
-
return
|
|
16736
|
-
return
|
|
17674
|
+
function stripDiffPrefix4(line2) {
|
|
17675
|
+
if (line2.startsWith("+") && !line2.startsWith("+++"))
|
|
17676
|
+
return line2.slice(1);
|
|
17677
|
+
if (line2.startsWith(" "))
|
|
17678
|
+
return line2.slice(1);
|
|
17679
|
+
return line2;
|
|
16737
17680
|
}
|
|
16738
17681
|
|
|
16739
17682
|
// dist/changeset.js
|
|
@@ -17269,8 +18212,36 @@ function cleanRunSummary(verdict, scope) {
|
|
|
17269
18212
|
return `[Halfcycle CI] ${verdict} ${files}, ${scope.signals} signal(s) checked.`;
|
|
17270
18213
|
}
|
|
17271
18214
|
|
|
18215
|
+
// dist/pinned-engagement.js
|
|
18216
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
18217
|
+
import { resolve } from "node:path";
|
|
18218
|
+
function readPinnedId(root) {
|
|
18219
|
+
let raw;
|
|
18220
|
+
try {
|
|
18221
|
+
raw = readFileSync5(resolve(root, BUNDLE_PIN_REL), "utf-8");
|
|
18222
|
+
} catch {
|
|
18223
|
+
return { problem: "no-pin" };
|
|
18224
|
+
}
|
|
18225
|
+
let pin;
|
|
18226
|
+
try {
|
|
18227
|
+
pin = JSON.parse(raw);
|
|
18228
|
+
} catch {
|
|
18229
|
+
return { problem: "no-id" };
|
|
18230
|
+
}
|
|
18231
|
+
const id = typeof pin === "object" && pin !== null ? pin[PIN_ENGAGEMENT_ID_FIELD] : void 0;
|
|
18232
|
+
return typeof id === "string" && id !== "" ? { id } : { problem: "no-id" };
|
|
18233
|
+
}
|
|
18234
|
+
function pinnedEngagementId(root) {
|
|
18235
|
+
const pinned = readPinnedId(root);
|
|
18236
|
+
return "id" in pinned && isEngagementId(pinned.id) ? pinned.id : void 0;
|
|
18237
|
+
}
|
|
18238
|
+
function projectFolder(env = process.env) {
|
|
18239
|
+
const dir = env["CLAUDE_PROJECT_DIR"];
|
|
18240
|
+
return dir !== void 0 && dir !== "" ? dir : process.cwd();
|
|
18241
|
+
}
|
|
18242
|
+
|
|
17272
18243
|
// dist/credential-store.js
|
|
17273
|
-
import { chmodSync, existsSync as existsSync3, readFileSync as
|
|
18244
|
+
import { chmodSync, existsSync as existsSync3, readFileSync as readFileSync6, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2, realpathSync } from "node:fs";
|
|
17274
18245
|
import { homedir as homedir2, platform } from "node:os";
|
|
17275
18246
|
import { dirname as dirname3, join as join5 } from "node:path";
|
|
17276
18247
|
var REFRESHED_TOKEN_KEYS = ["HALFCYCLE_TOKEN", "GUARD_SERVICE_TOKEN"];
|
|
@@ -17284,7 +18255,7 @@ function hasNotThisAccountMarker(engagementId, home) {
|
|
|
17284
18255
|
function readAccountCredential(controlOrigin, home) {
|
|
17285
18256
|
let raw;
|
|
17286
18257
|
try {
|
|
17287
|
-
raw =
|
|
18258
|
+
raw = readFileSync6(join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ACCOUNT_STORE_FILENAME), "utf-8");
|
|
17288
18259
|
} catch {
|
|
17289
18260
|
return void 0;
|
|
17290
18261
|
}
|
|
@@ -17301,7 +18272,7 @@ function readStoredEngagementToken(envFilePath) {
|
|
|
17301
18272
|
function readEngagementStore(envFilePath) {
|
|
17302
18273
|
let raw;
|
|
17303
18274
|
try {
|
|
17304
|
-
raw =
|
|
18275
|
+
raw = readFileSync6(envFilePath, "utf-8");
|
|
17305
18276
|
} catch {
|
|
17306
18277
|
return void 0;
|
|
17307
18278
|
}
|
|
@@ -17331,7 +18302,7 @@ function writeRefreshedToken(opts) {
|
|
|
17331
18302
|
}
|
|
17332
18303
|
let existing;
|
|
17333
18304
|
try {
|
|
17334
|
-
existing =
|
|
18305
|
+
existing = readFileSync6(declaredReal, "utf-8");
|
|
17335
18306
|
} catch {
|
|
17336
18307
|
existing = null;
|
|
17337
18308
|
}
|
|
@@ -17358,6 +18329,9 @@ function writeRefreshedToken(opts) {
|
|
|
17358
18329
|
// dist/credential-refresh.js
|
|
17359
18330
|
var CONTROL_TIMEOUT_MS = 5e3;
|
|
17360
18331
|
var CONNECTION_CHECK_DEADLINE_MS = 3e3;
|
|
18332
|
+
var SCOPE_NOTICE_DEADLINE_MS = 1500;
|
|
18333
|
+
var SESSION_START_HOOK_TIMEOUT_MS = 1e4;
|
|
18334
|
+
var SESSION_START_MARGIN_MS = 1500;
|
|
17361
18335
|
var UNAUTHORIZED = 401;
|
|
17362
18336
|
async function probeAccount(controlOrigin, credential, fetchFn = globalThis.fetch) {
|
|
17363
18337
|
const controller = new AbortController();
|
|
@@ -17471,7 +18445,7 @@ async function evaluateWithRefresh(opts) {
|
|
|
17471
18445
|
|
|
17472
18446
|
// dist/hooks/marker.js
|
|
17473
18447
|
import { createHash } from "node:crypto";
|
|
17474
|
-
import { existsSync as existsSync4, readFileSync as
|
|
18448
|
+
import { existsSync as existsSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3, statSync } from "node:fs";
|
|
17475
18449
|
import { tmpdir } from "node:os";
|
|
17476
18450
|
import { join as join6 } from "node:path";
|
|
17477
18451
|
var MARKER_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -17503,7 +18477,7 @@ function readMarker(repoRoot, sessionId) {
|
|
|
17503
18477
|
if (!existsSync4(path))
|
|
17504
18478
|
return null;
|
|
17505
18479
|
try {
|
|
17506
|
-
return
|
|
18480
|
+
return readFileSync7(path, "utf-8").trim() || null;
|
|
17507
18481
|
} catch {
|
|
17508
18482
|
return null;
|
|
17509
18483
|
}
|
|
@@ -17549,7 +18523,7 @@ function readDedupe(repoRoot, sessionId) {
|
|
|
17549
18523
|
if (!existsSync4(path))
|
|
17550
18524
|
return null;
|
|
17551
18525
|
try {
|
|
17552
|
-
const parsed = JSON.parse(
|
|
18526
|
+
const parsed = JSON.parse(readFileSync7(path, "utf-8"));
|
|
17553
18527
|
if (typeof parsed === "object" && parsed !== null && typeof parsed.base === "string" && typeof parsed.digest === "string") {
|
|
17554
18528
|
return parsed;
|
|
17555
18529
|
}
|
|
@@ -17594,7 +18568,7 @@ function orphanSweep(repoRoot, refKnown, keepSessionId, now = Date.now()) {
|
|
|
17594
18568
|
const full = join6(dir, name);
|
|
17595
18569
|
let unusable = false;
|
|
17596
18570
|
try {
|
|
17597
|
-
const ref =
|
|
18571
|
+
const ref = readFileSync7(full, "utf-8").trim();
|
|
17598
18572
|
if (!ref || !refKnown(ref))
|
|
17599
18573
|
unusable = true;
|
|
17600
18574
|
} catch {
|
|
@@ -17618,9 +18592,9 @@ function orphanSweep(repoRoot, refKnown, keepSessionId, now = Date.now()) {
|
|
|
17618
18592
|
}
|
|
17619
18593
|
|
|
17620
18594
|
// dist/seat-configuration.js
|
|
17621
|
-
import { existsSync as existsSync5, readFileSync as
|
|
18595
|
+
import { existsSync as existsSync5, readFileSync as readFileSync8, realpathSync as realpathSync2 } from "node:fs";
|
|
17622
18596
|
import { homedir as homedir3 } from "node:os";
|
|
17623
|
-
import { join as join7, relative as relative3, resolve } from "node:path";
|
|
18597
|
+
import { join as join7, relative as relative3, resolve as resolve2 } from "node:path";
|
|
17624
18598
|
function gatherSeatConfiguration(repoRoot, read = readSeatConfiguration) {
|
|
17625
18599
|
try {
|
|
17626
18600
|
const facts = read(repoRoot);
|
|
@@ -17633,7 +18607,7 @@ function gatherSeatConfiguration(repoRoot, read = readSeatConfiguration) {
|
|
|
17633
18607
|
}
|
|
17634
18608
|
}
|
|
17635
18609
|
function readSeatConfiguration(repoRoot) {
|
|
17636
|
-
const given =
|
|
18610
|
+
const given = resolve2(process.env["CLAUDE_PROJECT_DIR"] || process.cwd());
|
|
17637
18611
|
const projectDir = realpathSync2.native(given);
|
|
17638
18612
|
const home = homedir3();
|
|
17639
18613
|
let realHome = home;
|
|
@@ -17649,13 +18623,13 @@ function readSeatConfiguration(repoRoot) {
|
|
|
17649
18623
|
paths: readPathStates(projectDir, repoRoot)
|
|
17650
18624
|
};
|
|
17651
18625
|
}
|
|
17652
|
-
function
|
|
18626
|
+
function isPlainObject3(value) {
|
|
17653
18627
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17654
18628
|
}
|
|
17655
18629
|
function readSettingsFile(projectDir, rel) {
|
|
17656
18630
|
let raw;
|
|
17657
18631
|
try {
|
|
17658
|
-
raw =
|
|
18632
|
+
raw = readFileSync8(join7(projectDir, rel), "utf-8");
|
|
17659
18633
|
} catch (err) {
|
|
17660
18634
|
const code = err.code;
|
|
17661
18635
|
const state = code === "ENOENT" || code === "ENOTDIR" ? "absent" : "unreadable";
|
|
@@ -17667,12 +18641,12 @@ function readSettingsFile(projectDir, rel) {
|
|
|
17667
18641
|
} catch {
|
|
17668
18642
|
return { path: rel, state: "unreadable", hooks: [] };
|
|
17669
18643
|
}
|
|
17670
|
-
if (!
|
|
18644
|
+
if (!isPlainObject3(parsed))
|
|
17671
18645
|
return { path: rel, state: "unreadable", hooks: [] };
|
|
17672
18646
|
return { path: rel, state: "read", hooks: ourHooks(parsed["hooks"]) };
|
|
17673
18647
|
}
|
|
17674
18648
|
function ourHooks(hooks) {
|
|
17675
|
-
if (!
|
|
18649
|
+
if (!isPlainObject3(hooks))
|
|
17676
18650
|
return [];
|
|
17677
18651
|
const rows = [];
|
|
17678
18652
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -17680,7 +18654,7 @@ function ourHooks(hooks) {
|
|
|
17680
18654
|
if (!Array.isArray(entries))
|
|
17681
18655
|
continue;
|
|
17682
18656
|
for (const entry of entries) {
|
|
17683
|
-
if (!
|
|
18657
|
+
if (!isPlainObject3(entry))
|
|
17684
18658
|
continue;
|
|
17685
18659
|
let matcher = null;
|
|
17686
18660
|
if (Object.hasOwn(entry, "matcher")) {
|
|
@@ -17692,7 +18666,7 @@ function ourHooks(hooks) {
|
|
|
17692
18666
|
if (!Array.isArray(commands))
|
|
17693
18667
|
continue;
|
|
17694
18668
|
for (const hook of commands) {
|
|
17695
|
-
if (!
|
|
18669
|
+
if (!isPlainObject3(hook))
|
|
17696
18670
|
continue;
|
|
17697
18671
|
const command = hook["command"];
|
|
17698
18672
|
if (typeof command !== "string" || !HALFCYCLE_HOOK_COMMANDS.has(command))
|
|
@@ -17813,7 +18787,7 @@ async function evaluateEdit(reply, readSeat) {
|
|
|
17813
18787
|
await emitRunRecord({ runType: "hook", outcome: "unconfigured", changeSet });
|
|
17814
18788
|
if (!firstNoticeThisSession("credential-missing", repoRoot, input.session_id))
|
|
17815
18789
|
return;
|
|
17816
|
-
emitNeverConfigured(UNCONFIGURED_EXIT_TOOL_USE, readNeverConfiguredReason());
|
|
18790
|
+
emitNeverConfigured(UNCONFIGURED_EXIT_TOOL_USE, readNeverConfiguredReason(), pinnedEngagementId(projectFolder()));
|
|
17817
18791
|
return;
|
|
17818
18792
|
}
|
|
17819
18793
|
const { guardServiceUrl, guardServiceToken, guardEngagementId } = configResult.config;
|
|
@@ -17906,10 +18880,10 @@ ${guardLines}${notRunBlock}`);
|
|
|
17906
18880
|
}
|
|
17907
18881
|
}
|
|
17908
18882
|
function readStdin() {
|
|
17909
|
-
return new Promise((
|
|
18883
|
+
return new Promise((resolve5, reject) => {
|
|
17910
18884
|
const chunks = [];
|
|
17911
18885
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
17912
|
-
process.stdin.on("end", () =>
|
|
18886
|
+
process.stdin.on("end", () => resolve5(Buffer.concat(chunks).toString("utf-8")));
|
|
17913
18887
|
process.stdin.on("error", reject);
|
|
17914
18888
|
});
|
|
17915
18889
|
}
|
|
@@ -17994,7 +18968,7 @@ async function evaluateSession(input, kind, reply, readSeat) {
|
|
|
17994
18968
|
await emitRunRecord({ runType: "hook", outcome: "unconfigured", changeSet, diffBase });
|
|
17995
18969
|
if (!firstNoticeThisSession("credential-missing", repoRoot, sessionId))
|
|
17996
18970
|
return;
|
|
17997
|
-
emitNeverConfigured(UNCONFIGURED_EXIT_STOP_FAMILY, readNeverConfiguredReason());
|
|
18971
|
+
emitNeverConfigured(UNCONFIGURED_EXIT_STOP_FAMILY, readNeverConfiguredReason(), pinnedEngagementId(projectFolder()));
|
|
17998
18972
|
return;
|
|
17999
18973
|
}
|
|
18000
18974
|
const { guardServiceUrl, guardServiceToken, guardEngagementId } = configResult.config;
|
|
@@ -18105,9 +19079,9 @@ async function readHookStdin() {
|
|
|
18105
19079
|
}
|
|
18106
19080
|
}
|
|
18107
19081
|
function readStdin2() {
|
|
18108
|
-
return new Promise((
|
|
19082
|
+
return new Promise((resolve5) => {
|
|
18109
19083
|
if (process.stdin.isTTY) {
|
|
18110
|
-
|
|
19084
|
+
resolve5("");
|
|
18111
19085
|
return;
|
|
18112
19086
|
}
|
|
18113
19087
|
const chunks = [];
|
|
@@ -18117,7 +19091,7 @@ function readStdin2() {
|
|
|
18117
19091
|
return;
|
|
18118
19092
|
settled = true;
|
|
18119
19093
|
clearTimeout(timer);
|
|
18120
|
-
|
|
19094
|
+
resolve5(Buffer.concat(chunks).toString("utf-8"));
|
|
18121
19095
|
};
|
|
18122
19096
|
const timer = setTimeout(done, HOOK_STDIN_GRACE_MS);
|
|
18123
19097
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -18138,18 +19112,237 @@ async function runStop() {
|
|
|
18138
19112
|
await runSessionDiff(input, "stop");
|
|
18139
19113
|
}
|
|
18140
19114
|
|
|
18141
|
-
// dist/
|
|
18142
|
-
import {
|
|
18143
|
-
import { join as join8, sep as sep2 } from "node:path";
|
|
19115
|
+
// dist/hooks/user-prompt-submit.js
|
|
19116
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
18144
19117
|
import { homedir as homedir4 } from "node:os";
|
|
19118
|
+
import { join as join8 } from "node:path";
|
|
19119
|
+
|
|
19120
|
+
// dist/hooks/scope-phrase.js
|
|
19121
|
+
var APPROVE_PREFIX = /^approve /i;
|
|
19122
|
+
var SEND_BACK_PREFIX = /^send back /i;
|
|
19123
|
+
var LINE_BREAK = /[\n\r\u000b\u000c\u0085\u2028\u2029]/;
|
|
19124
|
+
function scopePhraseShape(prompt) {
|
|
19125
|
+
if (typeof prompt !== "string")
|
|
19126
|
+
return null;
|
|
19127
|
+
const line2 = prompt.trim();
|
|
19128
|
+
if (LINE_BREAK.test(line2))
|
|
19129
|
+
return null;
|
|
19130
|
+
const approve = APPROVE_PREFIX.exec(line2);
|
|
19131
|
+
if (approve)
|
|
19132
|
+
return { act: "approve", rest: line2.slice(approve[0].length) };
|
|
19133
|
+
const sendBack = SEND_BACK_PREFIX.exec(line2);
|
|
19134
|
+
if (sendBack)
|
|
19135
|
+
return { act: "send-back", rest: line2.slice(sendBack[0].length) };
|
|
19136
|
+
return null;
|
|
19137
|
+
}
|
|
19138
|
+
function matchScopePhraseShape(shape, shown) {
|
|
19139
|
+
if (shape.act === "approve") {
|
|
19140
|
+
const entry = resolvePhaseReference(shape.rest, shown);
|
|
19141
|
+
return entry === null ? { kind: "show-first", act: "approve" } : { kind: "approve", entry, reference: shape.rest };
|
|
19142
|
+
}
|
|
19143
|
+
for (let colon = shape.rest.lastIndexOf(":"); colon > 0; colon = shape.rest.lastIndexOf(":", colon - 1)) {
|
|
19144
|
+
const reference = shape.rest.slice(0, colon);
|
|
19145
|
+
const entry = resolvePhaseReference(reference, shown);
|
|
19146
|
+
if (entry !== null) {
|
|
19147
|
+
return { kind: "send-back", entry, reference, note: shape.rest.slice(colon + 1).trim() };
|
|
19148
|
+
}
|
|
19149
|
+
}
|
|
19150
|
+
return { kind: "show-first", act: "send-back" };
|
|
19151
|
+
}
|
|
19152
|
+
|
|
19153
|
+
// dist/hooks/user-prompt-submit-lines.js
|
|
19154
|
+
var TELL_THE_PERSON = "Tell the person.";
|
|
19155
|
+
function line(text) {
|
|
19156
|
+
return `${text} ${TELL_THE_PERSON}`;
|
|
19157
|
+
}
|
|
19158
|
+
function nothingDone(act) {
|
|
19159
|
+
return act === "approve" ? "Nothing was approved" : "Nothing was sent back";
|
|
19160
|
+
}
|
|
19161
|
+
function showCommand(phase) {
|
|
19162
|
+
return `\`${HALFCYCLE_COMMAND} plan ${quoteForShell(phase)} --show\``;
|
|
19163
|
+
}
|
|
19164
|
+
function recordedLine(act, phase, planVersion) {
|
|
19165
|
+
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.`);
|
|
19166
|
+
}
|
|
19167
|
+
function notAttendedLine(act) {
|
|
19168
|
+
return line(`${nothingDone(act)}: approving or sending back by typing counts only when a person types it into an open session.`);
|
|
19169
|
+
}
|
|
19170
|
+
function notSignedInLine(act) {
|
|
19171
|
+
return line(`${nothingDone(act)}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
|
|
19172
|
+
}
|
|
19173
|
+
function notOwnerLine(act) {
|
|
19174
|
+
return line(`${nothingDone(act)}: only the project's owner can approve or send back this scope.`);
|
|
19175
|
+
}
|
|
19176
|
+
function showFirstLine(act) {
|
|
19177
|
+
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.`);
|
|
19178
|
+
}
|
|
19179
|
+
function tooOldLine(act, phase) {
|
|
19180
|
+
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.`);
|
|
19181
|
+
}
|
|
19182
|
+
function noteNeededLine() {
|
|
19183
|
+
return line("Nothing was sent back: a send-back needs a note after the colon saying what should change.");
|
|
19184
|
+
}
|
|
19185
|
+
function noteLooksSecretLine() {
|
|
19186
|
+
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.");
|
|
19187
|
+
}
|
|
19188
|
+
function noteTooLongLine() {
|
|
19189
|
+
return line("Nothing was sent back: the note is too long. Shorten it and type the send-back again.");
|
|
19190
|
+
}
|
|
19191
|
+
function noteRefusedLine() {
|
|
19192
|
+
return line("Nothing was sent back: the note was not accepted. Reword it in plain words and type the send-back again.");
|
|
19193
|
+
}
|
|
19194
|
+
function approveRefusedLine() {
|
|
19195
|
+
return line(`${nothingDone("approve")}: Halfcycle did not accept the request. ${CLIENT_UPDATE_SENTENCE}`);
|
|
19196
|
+
}
|
|
19197
|
+
function cannotReachLine(act) {
|
|
19198
|
+
return line(`${nothingDone(act)}: Halfcycle could not be reached just now. Type the line again in a minute.`);
|
|
19199
|
+
}
|
|
19200
|
+
function lineForAnswer(act, phase, answer) {
|
|
19201
|
+
switch (answer.outcome) {
|
|
19202
|
+
case "recorded":
|
|
19203
|
+
return recordedLine(answer.act, answer.phase, answer.planVersion);
|
|
19204
|
+
case "show-first":
|
|
19205
|
+
return showFirstLine(act);
|
|
19206
|
+
case "plan-changed":
|
|
19207
|
+
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.`);
|
|
19208
|
+
case "already-approved":
|
|
19209
|
+
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.`);
|
|
19210
|
+
case "build-started":
|
|
19211
|
+
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.`);
|
|
19212
|
+
case "not-owner":
|
|
19213
|
+
return notOwnerLine(act);
|
|
19214
|
+
case "no-owner":
|
|
19215
|
+
return line(`${nothingDone(act)}: this project has no owner, so nobody can approve or send back its scope.`);
|
|
19216
|
+
case "not-offered":
|
|
19217
|
+
return line(`${nothingDone(act)}. ${TYPED_APPROVAL_OFF_SENTENCE}`);
|
|
19218
|
+
case "refused":
|
|
19219
|
+
return act === "approve" ? approveRefusedLine() : noteRefusedLine();
|
|
19220
|
+
case "unavailable":
|
|
19221
|
+
return cannotReachLine(act);
|
|
19222
|
+
default: {
|
|
19223
|
+
const unreachable = answer;
|
|
19224
|
+
return unreachable;
|
|
19225
|
+
}
|
|
19226
|
+
}
|
|
19227
|
+
}
|
|
19228
|
+
|
|
19229
|
+
// dist/hooks/user-prompt-submit.js
|
|
19230
|
+
var CHAT_REQUEST_TIMEOUT_MS = 6e3;
|
|
19231
|
+
var SHOWN_ENTRY_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
19232
|
+
async function runUserPromptSubmit(opts) {
|
|
19233
|
+
let act = null;
|
|
19234
|
+
try {
|
|
19235
|
+
const prompt = promptOf(opts.input);
|
|
19236
|
+
const shape = scopePhraseShape(prompt);
|
|
19237
|
+
if (shape === null)
|
|
19238
|
+
return "";
|
|
19239
|
+
act = shape.act;
|
|
19240
|
+
return `${await decide(shape, opts)}
|
|
19241
|
+
`;
|
|
19242
|
+
} catch {
|
|
19243
|
+
return act === null ? "" : `${cannotReachLine(act)}
|
|
19244
|
+
`;
|
|
19245
|
+
}
|
|
19246
|
+
}
|
|
19247
|
+
async function decide(shape, opts) {
|
|
19248
|
+
const { act } = shape;
|
|
19249
|
+
const env = opts.env ?? process.env;
|
|
19250
|
+
if (!readSessionAttended(env))
|
|
19251
|
+
return notAttendedLine(act);
|
|
19252
|
+
const read = readConfig(env);
|
|
19253
|
+
if (read.config === null) {
|
|
19254
|
+
return readNeverConfiguredReason(env) === "not-this-account" ? notOwnerLine(act) : notSignedInLine(act);
|
|
19255
|
+
}
|
|
19256
|
+
const { guardServiceToken, guardEngagementId } = read.config;
|
|
19257
|
+
const shown = readShownPlans(guardEngagementId, opts.home);
|
|
19258
|
+
const match = matchScopePhraseShape(shape, shown);
|
|
19259
|
+
if (match.kind === "show-first")
|
|
19260
|
+
return showFirstLine(act);
|
|
19261
|
+
const { entry } = match;
|
|
19262
|
+
if (!shownRecently(entry, (opts.now ?? Date.now)()))
|
|
19263
|
+
return tooOldLine(act, entry.phase);
|
|
19264
|
+
let candidate;
|
|
19265
|
+
if (match.kind === "send-back") {
|
|
19266
|
+
if (match.note === "")
|
|
19267
|
+
return noteNeededLine();
|
|
19268
|
+
if (!scopeNoteWireSchema.safeParse(match.note).success)
|
|
19269
|
+
return noteTooLongLine();
|
|
19270
|
+
if (looksLikeSecret(match.note))
|
|
19271
|
+
return noteLooksSecretLine();
|
|
19272
|
+
candidate = { act: "send-back", reference: match.reference, planVersion: entry.version, note: match.note };
|
|
19273
|
+
} else {
|
|
19274
|
+
candidate = { act: "approve", reference: match.reference, planVersion: entry.version };
|
|
19275
|
+
}
|
|
19276
|
+
const request = chatScopeRequestSchema.safeParse(candidate);
|
|
19277
|
+
if (!request.success)
|
|
19278
|
+
return showFirstLine(act);
|
|
19279
|
+
return post(act, entry, request.data, guardEngagementId, guardServiceToken, env, opts.fetchFn);
|
|
19280
|
+
}
|
|
19281
|
+
function promptOf(input) {
|
|
19282
|
+
try {
|
|
19283
|
+
const parsed = JSON.parse(input);
|
|
19284
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
19285
|
+
return void 0;
|
|
19286
|
+
return parsed["prompt"];
|
|
19287
|
+
} catch {
|
|
19288
|
+
return void 0;
|
|
19289
|
+
}
|
|
19290
|
+
}
|
|
19291
|
+
function readShownPlans(engagementId, home) {
|
|
19292
|
+
if (!isEngagementId(engagementId))
|
|
19293
|
+
return [];
|
|
19294
|
+
let text;
|
|
19295
|
+
try {
|
|
19296
|
+
text = readFileSync9(join8(home ?? homedir4(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SHOWN_PLANS_FILENAME), "utf-8");
|
|
19297
|
+
} catch {
|
|
19298
|
+
return [];
|
|
19299
|
+
}
|
|
19300
|
+
return parseShownPlans(text);
|
|
19301
|
+
}
|
|
19302
|
+
function shownRecently(entry, now) {
|
|
19303
|
+
const age = now - Date.parse(entry.shownAt);
|
|
19304
|
+
return Number.isFinite(age) && age >= 0 && age < SHOWN_ENTRY_MAX_AGE_MS;
|
|
19305
|
+
}
|
|
19306
|
+
async function post(act, entry, request, engagementId, credential, env, fetchFn = globalThis.fetch) {
|
|
19307
|
+
const url2 = `${readControlOrigin(env).origin}/engagements/${encodeURIComponent(engagementId)}/phases/${encodeURIComponent(entry.phase)}/scope/chat`;
|
|
19308
|
+
const controller = new AbortController();
|
|
19309
|
+
const timer = setTimeout(() => controller.abort(), CHAT_REQUEST_TIMEOUT_MS);
|
|
19310
|
+
try {
|
|
19311
|
+
const response = await fetchFn(url2, {
|
|
19312
|
+
method: "POST",
|
|
19313
|
+
headers: { Authorization: `Bearer ${credential}`, "Content-Type": "application/json" },
|
|
19314
|
+
body: JSON.stringify(request),
|
|
19315
|
+
signal: controller.signal
|
|
19316
|
+
});
|
|
19317
|
+
if (response.status === 401)
|
|
19318
|
+
return notSignedInLine(act);
|
|
19319
|
+
if (response.status === 429 || response.status >= 500)
|
|
19320
|
+
return cannotReachLine(act);
|
|
19321
|
+
const parsed = chatScopeResultSchema.safeParse(await response.json());
|
|
19322
|
+
if (!parsed.success)
|
|
19323
|
+
return cannotReachLine(act);
|
|
19324
|
+
if (parsed.data.outcome === "recorded" && response.status !== 200)
|
|
19325
|
+
return cannotReachLine(act);
|
|
19326
|
+
return lineForAnswer(act, entry.phase, parsed.data);
|
|
19327
|
+
} catch {
|
|
19328
|
+
return cannotReachLine(act);
|
|
19329
|
+
} finally {
|
|
19330
|
+
clearTimeout(timer);
|
|
19331
|
+
}
|
|
19332
|
+
}
|
|
19333
|
+
|
|
19334
|
+
// dist/resolve-engagement.js
|
|
19335
|
+
import { existsSync as existsSync6, readFileSync as readFileSync10, realpathSync as realpathSync3 } from "node:fs";
|
|
19336
|
+
import { join as join9, sep as sep2 } from "node:path";
|
|
19337
|
+
import { homedir as homedir5 } from "node:os";
|
|
18145
19338
|
function defaultRegistryPath() {
|
|
18146
|
-
return
|
|
19339
|
+
return join9(homedir5(), ".halfcycle", "registry.json");
|
|
18147
19340
|
}
|
|
18148
19341
|
function readRegistry(registryPath = defaultRegistryPath()) {
|
|
18149
19342
|
if (!existsSync6(registryPath))
|
|
18150
19343
|
return {};
|
|
18151
19344
|
try {
|
|
18152
|
-
const parsed = JSON.parse(
|
|
19345
|
+
const parsed = JSON.parse(readFileSync10(registryPath, "utf-8"));
|
|
18153
19346
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
18154
19347
|
return {};
|
|
18155
19348
|
const out = {};
|
|
@@ -18197,39 +19390,42 @@ function runResolveEngagement(cwd, registryPath) {
|
|
|
18197
19390
|
}
|
|
18198
19391
|
|
|
18199
19392
|
// dist/refresh-credential.js
|
|
18200
|
-
import { readFileSync as
|
|
18201
|
-
import { resolve as
|
|
19393
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
19394
|
+
import { resolve as resolve4 } from "node:path";
|
|
18202
19395
|
|
|
18203
19396
|
// dist/mcp-headers.js
|
|
18204
|
-
import { readFileSync as
|
|
18205
|
-
import { homedir as
|
|
18206
|
-
import { dirname as dirname4, resolve as
|
|
19397
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
19398
|
+
import { homedir as homedir6 } from "node:os";
|
|
19399
|
+
import { dirname as dirname4, resolve as resolve3 } from "node:path";
|
|
18207
19400
|
function defaultWriteErr(text) {
|
|
18208
19401
|
process.stderr.write(text);
|
|
18209
19402
|
}
|
|
18210
|
-
function headerLine(bearer) {
|
|
18211
|
-
|
|
19403
|
+
function headerLine(bearer, clientVersion) {
|
|
19404
|
+
const header = { Authorization: `Bearer ${bearer}` };
|
|
19405
|
+
if (clientVersion !== void 0)
|
|
19406
|
+
header[CLIENT_VERSION_HEADER] = clientVersion;
|
|
19407
|
+
return `${JSON.stringify(header)}
|
|
18212
19408
|
`;
|
|
18213
19409
|
}
|
|
18214
19410
|
function projectRootFromBinary(binaryPath) {
|
|
18215
19411
|
const depth = VENDORED_BIN_REL.split("/").length - 1;
|
|
18216
|
-
return
|
|
19412
|
+
return resolve3(dirname4(binaryPath), ...Array(depth).fill(".."));
|
|
18217
19413
|
}
|
|
18218
|
-
function
|
|
19414
|
+
function readPinnedVersion(root) {
|
|
18219
19415
|
let raw;
|
|
18220
19416
|
try {
|
|
18221
|
-
raw =
|
|
19417
|
+
raw = readFileSync11(resolve3(root, BUNDLE_PIN_REL), "utf-8");
|
|
18222
19418
|
} catch {
|
|
18223
|
-
return
|
|
19419
|
+
return void 0;
|
|
18224
19420
|
}
|
|
18225
19421
|
let pin;
|
|
18226
19422
|
try {
|
|
18227
19423
|
pin = JSON.parse(raw);
|
|
18228
19424
|
} catch {
|
|
18229
|
-
return
|
|
19425
|
+
return void 0;
|
|
18230
19426
|
}
|
|
18231
|
-
const
|
|
18232
|
-
return typeof
|
|
19427
|
+
const version2 = typeof pin === "object" && pin !== null ? pin["version"] : void 0;
|
|
19428
|
+
return typeof version2 === "string" && version2 !== "" ? version2 : void 0;
|
|
18233
19429
|
}
|
|
18234
19430
|
function resolveStore(root, home) {
|
|
18235
19431
|
const pinned = readPinnedId(root);
|
|
@@ -18255,12 +19451,12 @@ function shownUrl(url2) {
|
|
|
18255
19451
|
return url2.replace(/[^\x21-\x7e]/g, "").slice(0, 200);
|
|
18256
19452
|
}
|
|
18257
19453
|
function sentences(lines) {
|
|
18258
|
-
return lines.map((
|
|
19454
|
+
return lines.map((line2) => `halfcycle: ${line2}
|
|
18259
19455
|
`).join("");
|
|
18260
19456
|
}
|
|
18261
19457
|
function badIdSentence(root) {
|
|
18262
19458
|
return sentences([
|
|
18263
|
-
`${
|
|
19459
|
+
`${resolve3(root, BUNDLE_PIN_REL)} names an engagement id that is not a Halfcycle project id,`,
|
|
18264
19460
|
'so no credential was read. Delete that file and run "npx halfcycle" again: this repository',
|
|
18265
19461
|
"is then set up as a new Halfcycle project."
|
|
18266
19462
|
]);
|
|
@@ -18274,7 +19470,7 @@ function storeProblemSentence(problem, root, home, engagementId) {
|
|
|
18274
19470
|
]);
|
|
18275
19471
|
case "no-id":
|
|
18276
19472
|
return sentences([
|
|
18277
|
-
`${
|
|
19473
|
+
`${resolve3(root, BUNDLE_PIN_REL)} names no engagement id.`,
|
|
18278
19474
|
'run "npx halfcycle" here to rewrite it.'
|
|
18279
19475
|
]);
|
|
18280
19476
|
case "bad-id":
|
|
@@ -18283,13 +19479,15 @@ function storeProblemSentence(problem, root, home, engagementId) {
|
|
|
18283
19479
|
return sentences([
|
|
18284
19480
|
"this project belongs to another Halfcycle account, so this machine has no",
|
|
18285
19481
|
"credential for it and the Halfcycle tools are not available here. Nothing is",
|
|
18286
|
-
"broken
|
|
18287
|
-
|
|
19482
|
+
"broken.",
|
|
19483
|
+
requestAccessSentence(engagementId),
|
|
19484
|
+
"If this account is wrong, sign in as the account that owns it and run",
|
|
19485
|
+
'"npx halfcycle" again.'
|
|
18288
19486
|
]);
|
|
18289
19487
|
case "no-credential":
|
|
18290
19488
|
return sentences([
|
|
18291
19489
|
`this machine holds no credential for engagement ${engagementId ?? ""}.`,
|
|
18292
|
-
`run "npx halfcycle" in this repository \u2014 it will sign you in through your browser if needed, and write the credential to ${
|
|
19490
|
+
`run "npx halfcycle" in this repository \u2014 it will sign you in through your browser if needed, and write the credential to ${resolve3(home, HALFCYCLE_DIR_NAME)}, outside this repository.`
|
|
18293
19491
|
]);
|
|
18294
19492
|
}
|
|
18295
19493
|
}
|
|
@@ -18312,7 +19510,7 @@ async function refreshWithinDeadline(source, opts, writeErr) {
|
|
|
18312
19510
|
}
|
|
18313
19511
|
async function chooseBearer(opts) {
|
|
18314
19512
|
const writeErr = opts.writeErr ?? defaultWriteErr;
|
|
18315
|
-
const home = opts.home ??
|
|
19513
|
+
const home = opts.home ?? homedir6();
|
|
18316
19514
|
const root = projectRootFromBinary(opts.binaryPath);
|
|
18317
19515
|
const environmentToken = opts.environmentTokenFirst ? readEnvironmentToken() : void 0;
|
|
18318
19516
|
const store = resolveStore(root, home);
|
|
@@ -18376,6 +19574,7 @@ async function chooseBearer(opts) {
|
|
|
18376
19574
|
}
|
|
18377
19575
|
async function mcpHeaders(opts) {
|
|
18378
19576
|
const writeErr = opts.writeErr ?? defaultWriteErr;
|
|
19577
|
+
const root = projectRootFromBinary(opts.binaryPath);
|
|
18379
19578
|
try {
|
|
18380
19579
|
return headerLine(await chooseBearer({
|
|
18381
19580
|
...opts,
|
|
@@ -18383,7 +19582,7 @@ async function mcpHeaders(opts) {
|
|
|
18383
19582
|
requestedUrl: readMcpServerUrl(),
|
|
18384
19583
|
renewal: "renew-here",
|
|
18385
19584
|
environmentTokenFirst: true
|
|
18386
|
-
}));
|
|
19585
|
+
}), readPinnedVersion(root));
|
|
18387
19586
|
} catch (err) {
|
|
18388
19587
|
try {
|
|
18389
19588
|
writeErr(sentences([
|
|
@@ -18391,9 +19590,9 @@ async function mcpHeaders(opts) {
|
|
|
18391
19590
|
`run "npx halfcycle" in this repository to rewrite this machine's credential.`
|
|
18392
19591
|
]));
|
|
18393
19592
|
} catch {
|
|
18394
|
-
return headerLine(NO_CREDENTIAL_PLACEHOLDER);
|
|
19593
|
+
return headerLine(NO_CREDENTIAL_PLACEHOLDER, readPinnedVersion(root));
|
|
18395
19594
|
}
|
|
18396
|
-
return headerLine(NO_CREDENTIAL_PLACEHOLDER);
|
|
19595
|
+
return headerLine(NO_CREDENTIAL_PLACEHOLDER, readPinnedVersion(root));
|
|
18397
19596
|
}
|
|
18398
19597
|
}
|
|
18399
19598
|
|
|
@@ -18441,7 +19640,7 @@ var INITIALIZE_BODY = JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initializ
|
|
|
18441
19640
|
function registeredMcpUrl(projectRoot) {
|
|
18442
19641
|
let parsed;
|
|
18443
19642
|
try {
|
|
18444
|
-
parsed = JSON.parse(
|
|
19643
|
+
parsed = JSON.parse(readFileSync12(resolve4(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
|
|
18445
19644
|
} catch {
|
|
18446
19645
|
return void 0;
|
|
18447
19646
|
}
|
|
@@ -18535,16 +19734,83 @@ async function checkConnection(renewedToken, opts) {
|
|
|
18535
19734
|
environmentTokenFirst: false
|
|
18536
19735
|
});
|
|
18537
19736
|
if (CREDENTIAL_PLACEHOLDERS.includes(bearer)) {
|
|
18538
|
-
return PLACEHOLDER_SENTENCES[bearer];
|
|
19737
|
+
return { sentence: PLACEHOLDER_SENTENCES[bearer], url: url2, bearer };
|
|
18539
19738
|
}
|
|
18540
19739
|
const outcome = await probeDelivery(url2, bearer, opts.fetchFn ?? globalThis.fetch, opts.checkDeadlineMs ?? CONNECTION_CHECK_DEADLINE_MS);
|
|
18541
|
-
return outcome ?? CONNECTED_SENTENCE;
|
|
19740
|
+
return { sentence: outcome ?? CONNECTED_SENTENCE, url: url2, bearer };
|
|
19741
|
+
}
|
|
19742
|
+
function positionRequestBody(engagementId) {
|
|
19743
|
+
return JSON.stringify({
|
|
19744
|
+
jsonrpc: "2.0",
|
|
19745
|
+
id: 1,
|
|
19746
|
+
method: "tools/call",
|
|
19747
|
+
params: { name: "halfcycle_position", arguments: { engagementId } }
|
|
19748
|
+
});
|
|
19749
|
+
}
|
|
19750
|
+
async function fetchScopeNotices(url2, bearer, engagementId, fetchFn, deadlineMs) {
|
|
19751
|
+
const controller = new AbortController();
|
|
19752
|
+
const timer = setTimeout(() => controller.abort(), deadlineMs);
|
|
19753
|
+
try {
|
|
19754
|
+
let res;
|
|
19755
|
+
try {
|
|
19756
|
+
res = await fetchFn(url2, {
|
|
19757
|
+
method: "POST",
|
|
19758
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
|
|
19759
|
+
body: positionRequestBody(engagementId),
|
|
19760
|
+
redirect: "manual",
|
|
19761
|
+
signal: controller.signal
|
|
19762
|
+
});
|
|
19763
|
+
} catch {
|
|
19764
|
+
return void 0;
|
|
19765
|
+
}
|
|
19766
|
+
if (res.status < 200 || res.status > 299) {
|
|
19767
|
+
discardBody(res);
|
|
19768
|
+
return void 0;
|
|
19769
|
+
}
|
|
19770
|
+
let text;
|
|
19771
|
+
try {
|
|
19772
|
+
text = await res.text();
|
|
19773
|
+
} catch {
|
|
19774
|
+
return void 0;
|
|
19775
|
+
}
|
|
19776
|
+
let rpc;
|
|
19777
|
+
try {
|
|
19778
|
+
rpc = JSON.parse(text);
|
|
19779
|
+
} catch {
|
|
19780
|
+
return void 0;
|
|
19781
|
+
}
|
|
19782
|
+
const result = typeof rpc === "object" && rpc !== null ? rpc["result"] : void 0;
|
|
19783
|
+
if (typeof result !== "object" || result === null || result["isError"] === true) {
|
|
19784
|
+
return void 0;
|
|
19785
|
+
}
|
|
19786
|
+
const content = result["content"];
|
|
19787
|
+
const first = Array.isArray(content) ? content[0] : void 0;
|
|
19788
|
+
const bodyText = typeof first === "object" && first !== null ? first["text"] : void 0;
|
|
19789
|
+
if (typeof bodyText !== "string")
|
|
19790
|
+
return void 0;
|
|
19791
|
+
let body;
|
|
19792
|
+
try {
|
|
19793
|
+
body = JSON.parse(bodyText);
|
|
19794
|
+
} catch {
|
|
19795
|
+
return void 0;
|
|
19796
|
+
}
|
|
19797
|
+
const scope = typeof body === "object" && body !== null ? body["scope"] : void 0;
|
|
19798
|
+
const parsed = scopeNoticesSchema.safeParse(scope);
|
|
19799
|
+
return parsed.success ? parsed.data : void 0;
|
|
19800
|
+
} finally {
|
|
19801
|
+
clearTimeout(timer);
|
|
19802
|
+
}
|
|
19803
|
+
}
|
|
19804
|
+
function scopeNoticeLines(scope) {
|
|
19805
|
+
const text = scopeNoticeText(scope, { offerTyping: scope.typedApprovalOffered ? "yes" : "off" });
|
|
19806
|
+
return [...text.sentBack, ...text.waiting];
|
|
18542
19807
|
}
|
|
18543
19808
|
function defaultWriteErr2(text) {
|
|
18544
19809
|
process.stderr.write(text);
|
|
18545
19810
|
}
|
|
18546
19811
|
async function runRefreshCredential(opts) {
|
|
18547
19812
|
const writeErr = opts.writeErr ?? defaultWriteErr2;
|
|
19813
|
+
const startedAt = Date.now();
|
|
18548
19814
|
try {
|
|
18549
19815
|
const renewalSaid = [];
|
|
18550
19816
|
let renewalOpen = true;
|
|
@@ -18558,13 +19824,28 @@ async function runRefreshCredential(opts) {
|
|
|
18558
19824
|
renewalOpen = false;
|
|
18559
19825
|
const parts = [];
|
|
18560
19826
|
if (CHECKED_SOURCES.has(input["source"])) {
|
|
18561
|
-
const
|
|
18562
|
-
if (
|
|
18563
|
-
parts.push(
|
|
19827
|
+
const connection = await checkConnection(renewedToken, { ...opts, writeErr });
|
|
19828
|
+
if (connection !== void 0) {
|
|
19829
|
+
parts.push(connection.sentence);
|
|
19830
|
+
if (connection.sentence === CONNECTED_SENTENCE) {
|
|
19831
|
+
const engagementId = readConfig().config?.guardEngagementId;
|
|
19832
|
+
if (engagementId !== void 0) {
|
|
19833
|
+
const remainingForMargin = SESSION_START_HOOK_TIMEOUT_MS - SESSION_START_MARGIN_MS - (Date.now() - startedAt);
|
|
19834
|
+
const positionDeadline = Math.min(opts.scopeNoticeDeadlineMs ?? SCOPE_NOTICE_DEADLINE_MS, remainingForMargin);
|
|
19835
|
+
if (positionDeadline > 0) {
|
|
19836
|
+
const scope = await fetchScopeNotices(connection.url, connection.bearer, engagementId, opts.fetchFn ?? globalThis.fetch, positionDeadline);
|
|
19837
|
+
if (scope !== void 0)
|
|
19838
|
+
parts.push(...scopeNoticeLines(scope));
|
|
19839
|
+
}
|
|
19840
|
+
}
|
|
19841
|
+
} else if (connection.sentence === CANNOT_REACH_SENTENCE || connection.sentence === SERVICE_PROBLEM_SENTENCE) {
|
|
19842
|
+
parts.push(SCOPE_UNCHECKED_SENTENCE);
|
|
19843
|
+
}
|
|
19844
|
+
}
|
|
18564
19845
|
}
|
|
18565
19846
|
if (readConfig().config === null) {
|
|
18566
19847
|
const remedySaid = parts.some(namesTheSignInCommand);
|
|
18567
|
-
parts.push(noCoverageStatement(readNeverConfiguredReason(), remedySaid));
|
|
19848
|
+
parts.push(noCoverageStatement(readNeverConfiguredReason(), remedySaid, pinnedEngagementId(projectRootFromBinary(opts.binaryPath))));
|
|
18568
19849
|
}
|
|
18569
19850
|
for (const said of renewalSaid) {
|
|
18570
19851
|
if (namesTheSignInCommand(said) && parts.some(namesTheSignInCommand))
|
|
@@ -18588,7 +19869,7 @@ async function runRefreshCredential(opts) {
|
|
|
18588
19869
|
import { fileURLToPath } from "node:url";
|
|
18589
19870
|
|
|
18590
19871
|
// dist/capture.js
|
|
18591
|
-
import { readFileSync as
|
|
19872
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync7 } from "node:fs";
|
|
18592
19873
|
import { dirname as dirname5 } from "node:path";
|
|
18593
19874
|
function runCapture(provider, endpoint, opts = {}) {
|
|
18594
19875
|
const repoRoot = opts.repoRoot ?? process.cwd();
|
|
@@ -18656,7 +19937,7 @@ function readInputFile(fromFile) {
|
|
|
18656
19937
|
if (!fromFile)
|
|
18657
19938
|
return void 0;
|
|
18658
19939
|
try {
|
|
18659
|
-
return
|
|
19940
|
+
return readFileSync13(fromFile, "utf-8");
|
|
18660
19941
|
} catch {
|
|
18661
19942
|
return void 0;
|
|
18662
19943
|
}
|
|
@@ -18681,8 +19962,8 @@ async function main() {
|
|
|
18681
19962
|
return;
|
|
18682
19963
|
}
|
|
18683
19964
|
if (cmd === "mcp-headers") {
|
|
18684
|
-
const
|
|
18685
|
-
process.stdout.write(
|
|
19965
|
+
const line2 = await mcpHeaders({ binaryPath: fileURLToPath(import.meta.url) });
|
|
19966
|
+
process.stdout.write(line2, () => process.exit(0));
|
|
18686
19967
|
return;
|
|
18687
19968
|
}
|
|
18688
19969
|
if (cmd === "resolve-engagement") {
|
|
@@ -18738,8 +20019,18 @@ Commit the fixture; the next guard evaluation of this endpoint will pass.
|
|
|
18738
20019
|
process.exit(process.exitCode ?? 0);
|
|
18739
20020
|
return;
|
|
18740
20021
|
}
|
|
20022
|
+
if (sub === "user-prompt-submit") {
|
|
20023
|
+
let reply = "";
|
|
20024
|
+
try {
|
|
20025
|
+
reply = await runUserPromptSubmit({ input: await readStdin2() });
|
|
20026
|
+
} catch {
|
|
20027
|
+
reply = "";
|
|
20028
|
+
}
|
|
20029
|
+
process.stdout.write(reply, () => process.exit(0));
|
|
20030
|
+
return;
|
|
20031
|
+
}
|
|
18741
20032
|
process.stderr.write(`halfcycle-runner: unknown hook "${sub ?? ""}"
|
|
18742
|
-
Valid hooks: post-tool-use, stop, subagent-stop
|
|
20033
|
+
Valid hooks: post-tool-use, stop, subagent-stop, user-prompt-submit
|
|
18743
20034
|
`);
|
|
18744
20035
|
process.exit(1);
|
|
18745
20036
|
return;
|
|
@@ -18749,6 +20040,7 @@ Commit the fixture; the next guard evaluation of this endpoint will pass.
|
|
|
18749
20040
|
halfcycle-runner hook post-tool-use
|
|
18750
20041
|
halfcycle-runner hook stop
|
|
18751
20042
|
halfcycle-runner hook subagent-stop
|
|
20043
|
+
halfcycle-runner hook user-prompt-submit
|
|
18752
20044
|
halfcycle-runner refresh-credential
|
|
18753
20045
|
halfcycle-runner mcp-headers
|
|
18754
20046
|
halfcycle-runner resolve-engagement <cwd>
|
|
@@ -18757,10 +20049,10 @@ Commit the fixture; the next guard evaluation of this endpoint will pass.
|
|
|
18757
20049
|
process.exit(1);
|
|
18758
20050
|
}
|
|
18759
20051
|
function readStdin3() {
|
|
18760
|
-
return new Promise((
|
|
20052
|
+
return new Promise((resolve5, reject) => {
|
|
18761
20053
|
const chunks = [];
|
|
18762
20054
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
18763
|
-
process.stdin.on("end", () =>
|
|
20055
|
+
process.stdin.on("end", () => resolve5(Buffer.concat(chunks).toString("utf-8")));
|
|
18764
20056
|
process.stdin.on("error", reject);
|
|
18765
20057
|
});
|
|
18766
20058
|
}
|