@brainervirus/workit-cli 1.2.0 → 1.2.1
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/dist/index.js +248 -74
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -51105,6 +51105,7 @@ var workerSchema = object({
|
|
|
51105
51105
|
assignment: assignmentSchema,
|
|
51106
51106
|
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
51107
51107
|
session: refSchema.nullable(),
|
|
51108
|
+
coordinator: refSchema.optional(),
|
|
51108
51109
|
report: workerReportSchema.nullable()
|
|
51109
51110
|
}).strict();
|
|
51110
51111
|
var ownerSchema = object({
|
|
@@ -51216,6 +51217,7 @@ var taskSummarySchema = object({
|
|
|
51216
51217
|
requirements: array(requirementEvaluationSchema),
|
|
51217
51218
|
writer: workspaceRecordSchema.shape.writer
|
|
51218
51219
|
}).strict();
|
|
51220
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
51219
51221
|
var exportBundleSchema = object({
|
|
51220
51222
|
schemaVersion: literal(1),
|
|
51221
51223
|
exportedAt: utc,
|
|
@@ -51237,11 +51239,15 @@ var taskOperations = {
|
|
|
51237
51239
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
51238
51240
|
intent: intentSchema
|
|
51239
51241
|
}),
|
|
51240
|
-
list: operation({
|
|
51242
|
+
list: operation({
|
|
51243
|
+
action: literal("list"),
|
|
51244
|
+
status: _enum(["open", "closed", "all"]).optional(),
|
|
51245
|
+
limit: number2().int().min(1).max(50).optional()
|
|
51246
|
+
}),
|
|
51241
51247
|
inspect: operation({
|
|
51242
51248
|
action: literal("inspect"),
|
|
51243
51249
|
...taskId,
|
|
51244
|
-
view: _enum(["summary", "full"])
|
|
51250
|
+
view: _enum(["summary", "full"]).optional()
|
|
51245
51251
|
}),
|
|
51246
51252
|
revise: operation({
|
|
51247
51253
|
action: literal("revise"),
|
|
@@ -51269,7 +51275,7 @@ var taskOperations = {
|
|
|
51269
51275
|
...taskId,
|
|
51270
51276
|
...revisions,
|
|
51271
51277
|
expectedWorkspaceRevision: revision.optional(),
|
|
51272
|
-
authorityRefs: array(refSchema)
|
|
51278
|
+
authorityRefs: array(refSchema).optional()
|
|
51273
51279
|
}),
|
|
51274
51280
|
close: operation({
|
|
51275
51281
|
action: literal("close"),
|
|
@@ -58253,6 +58259,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
|
|
|
58253
58259
|
}) && !value.split(/[\\/]/).includes("..");
|
|
58254
58260
|
var callerValue = (value) => ({ host: value.host, actor: value.actor });
|
|
58255
58261
|
var workerIdOf = (value) => value.workerId ?? null;
|
|
58262
|
+
var currentWriterOwnsTask = (task, workspace, caller) => {
|
|
58263
|
+
const owner = workspace.writer?.owner;
|
|
58264
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
58265
|
+
};
|
|
58256
58266
|
function assertProductWriteAllowed(input) {
|
|
58257
58267
|
if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
|
|
58258
58268
|
return failure("invalid_input", "invalid product write path");
|
|
@@ -58958,9 +58968,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
|
58958
58968
|
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
58959
58969
|
if (task.status !== "active")
|
|
58960
58970
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
58961
|
-
const
|
|
58962
|
-
if (!
|
|
58963
|
-
return
|
|
58971
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
58972
|
+
if (!workspace.ok)
|
|
58973
|
+
return workspace;
|
|
58974
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
58975
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
58964
58976
|
const standing = binding.standing;
|
|
58965
58977
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
58966
58978
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -59008,14 +59020,14 @@ var standingAutoApplies = (store, host, actor, operation) => {
|
|
|
59008
59020
|
const workspace = store.readWorkspace();
|
|
59009
59021
|
if (!listed.ok || !workspace.ok || !workspace.data)
|
|
59010
59022
|
return null;
|
|
59011
|
-
const
|
|
59012
|
-
if (
|
|
59023
|
+
const task = listed.data.find((task) => currentWriterOwnsTask(task, workspace.data, { host, actor }));
|
|
59024
|
+
if (!task)
|
|
59013
59025
|
return null;
|
|
59014
59026
|
const resolved = resolveAutoApproval(store.root);
|
|
59015
59027
|
if (resolved.status !== "on")
|
|
59016
59028
|
return null;
|
|
59017
59029
|
return {
|
|
59018
|
-
task
|
|
59030
|
+
task,
|
|
59019
59031
|
workspaceId: workspace.data.id,
|
|
59020
59032
|
workspaceRevision: workspace.data.revision,
|
|
59021
59033
|
cls,
|
|
@@ -59274,6 +59286,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
59274
59286
|
};
|
|
59275
59287
|
var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
|
|
59276
59288
|
var storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok);
|
|
59289
|
+
var approvalAuthorityMatches = (store, task, approval, current) => {
|
|
59290
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
59291
|
+
return false;
|
|
59292
|
+
if (provenanceMatches(approval, current))
|
|
59293
|
+
return true;
|
|
59294
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
59295
|
+
if (!session || session.kind !== "host")
|
|
59296
|
+
return false;
|
|
59297
|
+
const workspace = store.readWorkspace();
|
|
59298
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
59299
|
+
host: session.host,
|
|
59300
|
+
actor: session.handle
|
|
59301
|
+
});
|
|
59302
|
+
};
|
|
59277
59303
|
var validateAction = (store, task, workspaceId, input, authority) => {
|
|
59278
59304
|
if (task.status !== "active")
|
|
59279
59305
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
@@ -59299,7 +59325,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
59299
59325
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
59300
59326
|
if (decision.response !== "approved")
|
|
59301
59327
|
return failure("permission_denied", "decision was rejected");
|
|
59302
|
-
if (
|
|
59328
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
59303
59329
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
59304
59330
|
const receipts = entry.provenance.receipts;
|
|
59305
59331
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -59466,7 +59492,7 @@ function settleAction(input) {
|
|
|
59466
59492
|
return failure("not_found", "decision not found");
|
|
59467
59493
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
59468
59494
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
59469
|
-
if (!
|
|
59495
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
59470
59496
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
59471
59497
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
59472
59498
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -59565,7 +59591,7 @@ function reconcileAction(input) {
|
|
|
59565
59591
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
59566
59592
|
if (!entry || entry.data.purpose !== "action")
|
|
59567
59593
|
return failure("not_found", "decision not found");
|
|
59568
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
59594
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
59569
59595
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
59570
59596
|
if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
|
|
59571
59597
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -59902,7 +59928,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
|
59902
59928
|
candidateId: evidence.candidateId ?? fallback
|
|
59903
59929
|
};
|
|
59904
59930
|
};
|
|
59905
|
-
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
59931
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
|
|
59906
59932
|
var applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file")));
|
|
59907
59933
|
var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && (data.response === "approved" || data.response === "stated") && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
|
|
59908
59934
|
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
@@ -60255,6 +60281,11 @@ var exportDigest = (bundle) => sha256({
|
|
|
60255
60281
|
});
|
|
60256
60282
|
// packages/workit-core/src/core/external-action.ts
|
|
60257
60283
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
60284
|
+
var chainStepSchema = union2([
|
|
60285
|
+
string2().min(1),
|
|
60286
|
+
object({ branch: string2().min(1) }).strict(),
|
|
60287
|
+
object({ pr: literal(true) }).strict()
|
|
60288
|
+
]);
|
|
60258
60289
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
60259
60290
|
object({
|
|
60260
60291
|
operation: literal("git.branch_setup"),
|
|
@@ -60269,7 +60300,7 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
60269
60300
|
operation: literal("git.commit"),
|
|
60270
60301
|
payload: object({
|
|
60271
60302
|
message: string2().min(1).optional(),
|
|
60272
|
-
plan_steps: array(
|
|
60303
|
+
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
60273
60304
|
plan_branch: string2().min(1).optional()
|
|
60274
60305
|
}).strict()
|
|
60275
60306
|
}).strict(),
|
|
@@ -60373,7 +60404,7 @@ var planReservationLength = (operation, descriptorPayload) => {
|
|
|
60373
60404
|
const payload = descriptorPayload ?? {};
|
|
60374
60405
|
return Array.isArray(payload.plan_steps) ? payload.plan_steps.length : null;
|
|
60375
60406
|
};
|
|
60376
|
-
var externalActionHelp = "Fixed actions: git.branch_setup {action?,sdd_dir?,target_branch(required unless reapply_stash; the working branch to create or switch to),stash?}; git.commit {message}; git.push {branch?}; hosting.pull_request {title,body?,draft?,target_branch?,babysit?}; hosting.merge {target_branch?,source_branch?}; youtrack.update {issueId,markdown,minutes?}; youtrack.time {issueId,minutes,text?,dateMs?}; youtrack.meeting {issueId,minutes,text}; changelog.apply {entries?,path?,normalize_only?}; context.read {kind,range?,issueId?,issueUrl?,issueRef?,mode?,specPath?,planPath?}. context.read is read-only and needs no approval; all other operations require native approval (CLI uses a TTY; caller-unattested MCP cannot mutate).";
|
|
60407
|
+
var externalActionHelp = "Fixed actions: git.branch_setup {action?,sdd_dir?,target_branch(required unless reapply_stash; the working branch to create or switch to),stash?}; git.commit {message?; plan_steps?: (string | {branch:string} | {pr:true})[]; plan_branch?}; git.push {branch?}; hosting.pull_request {title,body?,draft?,target_branch?,babysit?}; hosting.merge {target_branch?,source_branch?}; youtrack.update {issueId,markdown,minutes?}; youtrack.time {issueId,minutes,text?,dateMs?}; youtrack.meeting {issueId,minutes,text}; changelog.apply {entries?,path?,normalize_only?}; context.read {kind: git|pr|youtrack|github_issue|gitlab_issue|changelog|release|affected,range?,issueId?,issueUrl?,issueRef?,mode?,specPath?,planPath?}. context.read is read-only and needs no approval; all other operations require native approval (CLI uses a TTY; caller-unattested MCP cannot mutate).";
|
|
60377
60408
|
var externalActionRef = (host, actor, descriptor) => ({
|
|
60378
60409
|
kind: "host",
|
|
60379
60410
|
host,
|
|
@@ -60387,12 +60418,12 @@ var externalActionState = (store, host, actor, operation) => {
|
|
|
60387
60418
|
if (!workspace.data)
|
|
60388
60419
|
return failure("not_found", "workspace not found");
|
|
60389
60420
|
const candidates = listed.data.flatMap((task) => {
|
|
60390
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
60421
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
60391
60422
|
return [];
|
|
60392
60423
|
return task.decisions.filter((entry) => entry.data.purpose === "action" && entry.data.response === "approved" && entry.data.revoked === null && entry.data.binding.approvedContent === operation).map((entry) => ({ task, entry }));
|
|
60393
60424
|
});
|
|
60394
60425
|
if (candidates.length !== 1)
|
|
60395
|
-
return failure("permission_denied", candidates.length === 0 ? "no approved action is bound to
|
|
60426
|
+
return failure("permission_denied", candidates.length === 0 ? "no approved action is bound to the active task" : "multiple approved actions match the active task");
|
|
60396
60427
|
return success(workspace.data.revision, null, { ...candidates[0], workspace: workspace.data });
|
|
60397
60428
|
};
|
|
60398
60429
|
var priorExternalAction = (store, host, actor, operation, payload) => {
|
|
@@ -60472,8 +60503,6 @@ var approvedPlanCommit = (store, host, actor, message) => {
|
|
|
60472
60503
|
const matches = listed.data.flatMap((task) => {
|
|
60473
60504
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
60474
60505
|
return [];
|
|
60475
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
60476
|
-
return [];
|
|
60477
60506
|
return task.decisions.flatMap((entry) => {
|
|
60478
60507
|
const decision = entry.data;
|
|
60479
60508
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -60515,8 +60544,6 @@ var approvedChainStep = (store, host, actor, query) => {
|
|
|
60515
60544
|
const matches = listed.data.flatMap((task) => {
|
|
60516
60545
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
60517
60546
|
return [];
|
|
60518
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
60519
|
-
return [];
|
|
60520
60547
|
return task.decisions.flatMap((entry) => {
|
|
60521
60548
|
const decision = entry.data;
|
|
60522
60549
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -60673,7 +60700,7 @@ var branchSetupIntent = (descriptor) => {
|
|
|
60673
60700
|
var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
60674
60701
|
const current = branchSetupIntent(operation);
|
|
60675
60702
|
if (!current)
|
|
60676
|
-
return failure("permission_denied", "no approved action is bound to
|
|
60703
|
+
return failure("permission_denied", "no approved action is bound to the active task");
|
|
60677
60704
|
const listed = store.listTasks();
|
|
60678
60705
|
const workspace = store.readWorkspace();
|
|
60679
60706
|
if (!listed.ok || !workspace.ok)
|
|
@@ -60681,7 +60708,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
60681
60708
|
if (!workspace.data)
|
|
60682
60709
|
return failure("not_found", "workspace not found");
|
|
60683
60710
|
const candidates = listed.data.flatMap((task) => {
|
|
60684
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
60711
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
60685
60712
|
return [];
|
|
60686
60713
|
return task.decisions.filter((entry) => entry.data.purpose === "action" && entry.data.response === "approved" && entry.data.revoked === null).map((entry) => ({ task, entry }));
|
|
60687
60714
|
});
|
|
@@ -60690,7 +60717,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
60690
60717
|
return intent !== null && intent.target === current.target && intent.base === current.base && intent.remoteBase === current.remoteBase && intent.targetExists === current.targetExists;
|
|
60691
60718
|
});
|
|
60692
60719
|
if (matches.length !== 1)
|
|
60693
|
-
return failure("permission_denied", matches.length === 0 ? "no approved action is bound to
|
|
60720
|
+
return failure("permission_denied", matches.length === 0 ? "no approved action is bound to the active task" : "multiple approved actions match the active task");
|
|
60694
60721
|
return success(workspace.data.revision, null, { ...matches[0], workspace: workspace.data });
|
|
60695
60722
|
};
|
|
60696
60723
|
var priorResolvedDrift = (store, host, actor, operation, payload, currentResolved) => {
|
|
@@ -61228,6 +61255,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
61228
61255
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
61229
61256
|
state: expected.state,
|
|
61230
61257
|
session: expected.session,
|
|
61258
|
+
report: expected.report ?? null,
|
|
61231
61259
|
provenance: result.data,
|
|
61232
61260
|
...binding
|
|
61233
61261
|
});
|
|
@@ -61238,7 +61266,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
61238
61266
|
verifiedWorkers.delete(token);
|
|
61239
61267
|
if (!value)
|
|
61240
61268
|
return null;
|
|
61241
|
-
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
|
|
61269
|
+
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
|
|
61242
61270
|
return null;
|
|
61243
61271
|
return value;
|
|
61244
61272
|
};
|
|
@@ -61279,6 +61307,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
61279
61307
|
return failure("invalid_transition", "worker already has an observed session");
|
|
61280
61308
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
61281
61309
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
61310
|
+
if (input.report && input.state !== "stopped")
|
|
61311
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
61282
61312
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
61283
61313
|
return failure("invalid_input", "running worker observations require a session");
|
|
61284
61314
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -61295,12 +61325,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
61295
61325
|
data: {
|
|
61296
61326
|
...entry.data,
|
|
61297
61327
|
state: terminalCancel ? "cancelling" : input.state,
|
|
61298
|
-
session: input.session
|
|
61328
|
+
session: input.session,
|
|
61329
|
+
report: entry.data.report ?? input.report ?? null
|
|
61299
61330
|
}
|
|
61300
61331
|
};
|
|
61301
61332
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
61302
61333
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
61303
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
61334
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
61304
61335
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
61305
61336
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
61306
61337
|
taskId: task.data.id,
|
|
@@ -61718,14 +61749,30 @@ class WorkitCore {
|
|
|
61718
61749
|
const listed = this.store.listTasks();
|
|
61719
61750
|
if (!listed.ok)
|
|
61720
61751
|
return listed;
|
|
61721
|
-
const
|
|
61722
|
-
|
|
61723
|
-
|
|
61724
|
-
|
|
61725
|
-
|
|
61726
|
-
|
|
61727
|
-
|
|
61728
|
-
|
|
61752
|
+
const status = input.status ?? "open";
|
|
61753
|
+
const limit = input.limit ?? 20;
|
|
61754
|
+
const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
61755
|
+
if (selected.length === 0)
|
|
61756
|
+
return success(null, null, []);
|
|
61757
|
+
const workspace = this.store.readWorkspace();
|
|
61758
|
+
if (!workspace.ok)
|
|
61759
|
+
return workspace;
|
|
61760
|
+
if (!workspace.data)
|
|
61761
|
+
return failure("not_found", "workspace not found");
|
|
61762
|
+
const tasks = selected.map((task) => ({
|
|
61763
|
+
id: task.id,
|
|
61764
|
+
revision: task.revision,
|
|
61765
|
+
workspaceRevision: workspace.data.revision,
|
|
61766
|
+
createdAt: task.createdAt,
|
|
61767
|
+
updatedAt: task.updatedAt,
|
|
61768
|
+
runtime: task.runtime ?? null,
|
|
61769
|
+
objective: task.intent.data.objective,
|
|
61770
|
+
status: task.status,
|
|
61771
|
+
closure: task.closure,
|
|
61772
|
+
progress: task.progress,
|
|
61773
|
+
writer: task.status === "closed" ? null : workspace.data.writer
|
|
61774
|
+
}));
|
|
61775
|
+
return success(null, null, tasks);
|
|
61729
61776
|
}
|
|
61730
61777
|
case "inspect": {
|
|
61731
61778
|
const task = this.store.readTask(input.taskId);
|
|
@@ -61734,7 +61781,7 @@ class WorkitCore {
|
|
|
61734
61781
|
const helper = this.helperTaskGuard(task.data, true);
|
|
61735
61782
|
if (!helper.ok)
|
|
61736
61783
|
return helper;
|
|
61737
|
-
if (input.view === "summary")
|
|
61784
|
+
if ((input.view ?? "summary") === "summary")
|
|
61738
61785
|
return this.summary(task.data);
|
|
61739
61786
|
return this.view(task.data);
|
|
61740
61787
|
}
|
|
@@ -62078,7 +62125,7 @@ class WorkitCore {
|
|
|
62078
62125
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
62079
62126
|
const verified = evidence.some((entry) => {
|
|
62080
62127
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
62081
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
62128
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
62082
62129
|
kind: entry.data.kind,
|
|
62083
62130
|
candidateId: entry.data.candidateId,
|
|
62084
62131
|
status: evaluation?.status ?? "missing"
|
|
@@ -62158,7 +62205,18 @@ class WorkitCore {
|
|
|
62158
62205
|
if (task.data.status !== "active")
|
|
62159
62206
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
62160
62207
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
62161
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
62208
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
62209
|
+
fields: [
|
|
62210
|
+
{
|
|
62211
|
+
path: "assignment.scope",
|
|
62212
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
62213
|
+
},
|
|
62214
|
+
{
|
|
62215
|
+
path: "task.intent.scope",
|
|
62216
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
62217
|
+
}
|
|
62218
|
+
]
|
|
62219
|
+
});
|
|
62162
62220
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
62163
62221
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
62164
62222
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -62365,7 +62423,15 @@ class WorkitCore {
|
|
|
62365
62423
|
...item,
|
|
62366
62424
|
recordedAt: mutation.now,
|
|
62367
62425
|
provenance: attested.data,
|
|
62368
|
-
data: {
|
|
62426
|
+
data: {
|
|
62427
|
+
...item.data,
|
|
62428
|
+
state: "dispatching",
|
|
62429
|
+
coordinator: {
|
|
62430
|
+
kind: "host",
|
|
62431
|
+
host: this.context.caller.host,
|
|
62432
|
+
handle: this.context.caller.actor
|
|
62433
|
+
}
|
|
62434
|
+
}
|
|
62369
62435
|
} : item)
|
|
62370
62436
|
});
|
|
62371
62437
|
}
|
|
@@ -62441,6 +62507,7 @@ class WorkitCore {
|
|
|
62441
62507
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
62442
62508
|
state: "stopped",
|
|
62443
62509
|
session: null,
|
|
62510
|
+
report: null,
|
|
62444
62511
|
provenance: attested.data,
|
|
62445
62512
|
owner: this.authorityOwner,
|
|
62446
62513
|
store: this.store,
|
|
@@ -62748,10 +62815,12 @@ class WorkitCore {
|
|
|
62748
62815
|
return workspace;
|
|
62749
62816
|
if (!workspace.data)
|
|
62750
62817
|
return failure("not_found", "workspace not found");
|
|
62751
|
-
const
|
|
62818
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
62819
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
62752
62820
|
if (!current.ok)
|
|
62753
62821
|
return current;
|
|
62754
|
-
const
|
|
62822
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
62823
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
62755
62824
|
return success(task.revision, workspace.data.revision, {
|
|
62756
62825
|
id: task.id,
|
|
62757
62826
|
revision: task.revision,
|
|
@@ -62765,7 +62834,7 @@ class WorkitCore {
|
|
|
62765
62834
|
progress: task.progress,
|
|
62766
62835
|
policy: task.policy,
|
|
62767
62836
|
requirements,
|
|
62768
|
-
writer:
|
|
62837
|
+
writer: evaluationWorkspace.writer
|
|
62769
62838
|
});
|
|
62770
62839
|
}
|
|
62771
62840
|
view(task) {
|
|
@@ -62774,13 +62843,15 @@ class WorkitCore {
|
|
|
62774
62843
|
return workspace;
|
|
62775
62844
|
if (!workspace.data)
|
|
62776
62845
|
return failure("not_found", "workspace not found");
|
|
62777
|
-
const
|
|
62846
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
62847
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
62778
62848
|
if (!current.ok)
|
|
62779
62849
|
return current;
|
|
62780
|
-
const
|
|
62850
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
62851
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
62781
62852
|
return success(task.revision, workspace.data.revision, {
|
|
62782
62853
|
task,
|
|
62783
|
-
workspace:
|
|
62854
|
+
workspace: evaluationWorkspace,
|
|
62784
62855
|
capabilities: this.context.capabilities,
|
|
62785
62856
|
requirements,
|
|
62786
62857
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -62871,8 +62942,6 @@ class WorkitCore {
|
|
|
62871
62942
|
return helper;
|
|
62872
62943
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
62873
62944
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
62874
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
62875
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
62876
62945
|
const workspace = this.store.readWorkspace();
|
|
62877
62946
|
if (!workspace.ok)
|
|
62878
62947
|
return workspace;
|
|
@@ -62902,7 +62971,7 @@ class WorkitCore {
|
|
|
62902
62971
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
62903
62972
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
62904
62973
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
62905
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
62974
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
62906
62975
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
62907
62976
|
resumeCandidate = current.data.candidate;
|
|
62908
62977
|
}
|
|
@@ -63032,7 +63101,7 @@ class WorkitCore {
|
|
|
63032
63101
|
task: (value, context) => success(context.revision, null, {
|
|
63033
63102
|
...value,
|
|
63034
63103
|
status: "closed",
|
|
63035
|
-
candidates:
|
|
63104
|
+
candidates: taskForView.candidates,
|
|
63036
63105
|
closure: {
|
|
63037
63106
|
outcome: closure.data.outcome,
|
|
63038
63107
|
at: context.now,
|
|
@@ -64911,7 +64980,7 @@ var readExternalContext = async (root, payload) => {
|
|
|
64911
64980
|
}
|
|
64912
64981
|
}
|
|
64913
64982
|
};
|
|
64914
|
-
var localAction = (operation) => operation === "git.branch_setup" || operation === "git.commit" || operation === "git.push" || operation === "hosting.pull_request" || operation === "changelog.apply";
|
|
64983
|
+
var localAction = (operation) => operation === "git.branch_setup" || operation === "git.commit" || operation === "git.push" || operation === "hosting.pull_request" || operation === "hosting.merge" || operation === "changelog.apply";
|
|
64915
64984
|
var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
64916
64985
|
const store = new TaskStore(root);
|
|
64917
64986
|
const listed = store.listTasks();
|
|
@@ -64922,7 +64991,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
64922
64991
|
return workspace;
|
|
64923
64992
|
if (!workspace.data)
|
|
64924
64993
|
return failure("not_found", "workspace not found");
|
|
64925
|
-
const candidates = listed.data.filter((task) => task
|
|
64994
|
+
const candidates = listed.data.filter((task) => currentWriterOwnsTask(task, workspace.data, caller));
|
|
64926
64995
|
if (candidates.length !== 1)
|
|
64927
64996
|
return failure("permission_denied", "local external action requires exactly one active task");
|
|
64928
64997
|
const allowed = assertProductWriteAllowed({
|
|
@@ -64932,7 +65001,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
64932
65001
|
paths,
|
|
64933
65002
|
store
|
|
64934
65003
|
});
|
|
64935
|
-
return allowed
|
|
65004
|
+
return allowed;
|
|
64936
65005
|
};
|
|
64937
65006
|
var safePath = (root, value) => {
|
|
64938
65007
|
try {
|
|
@@ -64944,13 +65013,20 @@ var safePath = (root, value) => {
|
|
|
64944
65013
|
var branchCreationBaseline = (root, target, base) => {
|
|
64945
65014
|
const exists = gitValue(root, ["rev-parse", "--verify", "--quiet", `refs/heads/${target}`]) !== null;
|
|
64946
65015
|
if (exists)
|
|
64947
|
-
return {
|
|
65016
|
+
return {
|
|
65017
|
+
base_branch: base,
|
|
65018
|
+
target_exists: true,
|
|
65019
|
+
remote_base: null,
|
|
65020
|
+
target_head: gitValue(root, ["rev-parse", `refs/heads/${target}`])
|
|
65021
|
+
};
|
|
64948
65022
|
const remote = run3(root, ["ls-remote", "origin", `refs/heads/${base}`]);
|
|
64949
65023
|
const sha = remote.exitCode === 0 ? remote.stdout.trim().split(/\s+/)[0] : "";
|
|
65024
|
+
const remoteBase = /^[0-9a-f]{40}$/.test(sha ?? "") ? sha : null;
|
|
64950
65025
|
return {
|
|
64951
65026
|
base_branch: base,
|
|
64952
65027
|
target_exists: false,
|
|
64953
|
-
remote_base:
|
|
65028
|
+
remote_base: remoteBase,
|
|
65029
|
+
target_head: remoteBase
|
|
64954
65030
|
};
|
|
64955
65031
|
};
|
|
64956
65032
|
var shortSha = (value) => typeof value === "string" && value ? value.slice(0, 8) : "unknown";
|
|
@@ -65061,7 +65137,7 @@ var actionProposalQuestion = (request, descriptorPayload) => {
|
|
|
65061
65137
|
var approvedResolvedExternalAction = (content) => {
|
|
65062
65138
|
try {
|
|
65063
65139
|
const value = JSON.parse(content);
|
|
65064
|
-
if (value.operation !== "hosting.pull_request" && value.operation !== "hosting.merge" && value.operation !== "changelog.apply" && value.operation !== "youtrack.update" && value.operation !== "youtrack.time" && value.operation !== "youtrack.meeting")
|
|
65140
|
+
if (value.operation !== "git.branch_setup" && value.operation !== "git.commit" && value.operation !== "git.push" && value.operation !== "hosting.pull_request" && value.operation !== "hosting.merge" && value.operation !== "changelog.apply" && value.operation !== "youtrack.update" && value.operation !== "youtrack.time" && value.operation !== "youtrack.meeting")
|
|
65065
65141
|
return failure("invalid_input", "approved action is not a reconciliable external action");
|
|
65066
65142
|
if (!value.payload || typeof value.payload !== "object")
|
|
65067
65143
|
return failure("invalid_input", "approved action payload is incomplete");
|
|
@@ -65072,18 +65148,24 @@ var approvedResolvedExternalAction = (content) => {
|
|
|
65072
65148
|
if (!request.ok || !resolved || typeof resolved !== "object")
|
|
65073
65149
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
65074
65150
|
const target = resolved;
|
|
65151
|
+
if (value.operation === "git.commit" && (typeof target.head !== "string" || typeof target.staged !== "string" || typeof payload.message !== "string"))
|
|
65152
|
+
return failure("invalid_input", "approved Git commit target is incomplete");
|
|
65153
|
+
if (value.operation === "git.push" && (typeof target.branch !== "string" || typeof target.commit !== "string" || typeof target.remote !== "string"))
|
|
65154
|
+
return failure("invalid_input", "approved Git push target is incomplete");
|
|
65155
|
+
if (value.operation === "git.branch_setup" && (typeof target.head !== "string" || (payload.action === "reapply_stash" ? typeof target.stash_commit !== "string" : typeof payload.target_branch !== "string" || typeof target.target_head !== "string")))
|
|
65156
|
+
return failure("invalid_input", "approved Git branch target is incomplete");
|
|
65075
65157
|
if (value.operation === "hosting.pull_request" && (typeof target.marker !== "string" || typeof target.source_branch !== "string" || typeof target.source_commit !== "string" || typeof target.remote !== "string" || typeof payload.target_branch !== "string"))
|
|
65076
65158
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
65077
65159
|
if (value.operation === "hosting.merge" && (typeof target.marker !== "string" || typeof target.source_branch !== "string" || typeof target.source_commit !== "string" || typeof target.remote !== "string" || typeof payload.target_branch !== "string"))
|
|
65078
65160
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
65079
65161
|
if (value.operation === "changelog.apply" && (typeof target.target !== "string" || typeof target.beforeDigest !== "string" || typeof target.beforeExists !== "boolean" || typeof target.afterDigest !== "string"))
|
|
65080
65162
|
return failure("invalid_input", "approved changelog target is incomplete");
|
|
65081
|
-
if (value.operation !== "hosting.pull_request" && value.operation !== "changelog.apply" && (typeof target.marker !== "string" || typeof target.baseUrl !== "string" || typeof target.issueId !== "string" || !Array.isArray(target.steps)))
|
|
65163
|
+
if (value.operation !== "git.branch_setup" && value.operation !== "git.commit" && value.operation !== "git.push" && value.operation !== "hosting.pull_request" && value.operation !== "hosting.merge" && value.operation !== "changelog.apply" && (typeof target.marker !== "string" || typeof target.baseUrl !== "string" || typeof target.issueId !== "string" || !Array.isArray(target.steps)))
|
|
65082
65164
|
return failure("invalid_input", "approved YouTrack target is incomplete");
|
|
65083
65165
|
return success(null, null, {
|
|
65084
65166
|
request: request.data,
|
|
65085
65167
|
descriptorPayload: value.payload,
|
|
65086
|
-
marker:
|
|
65168
|
+
...typeof target.marker === "string" ? { marker: target.marker } : {}
|
|
65087
65169
|
});
|
|
65088
65170
|
} catch {
|
|
65089
65171
|
return failure("invalid_input", "approved action descriptor is invalid");
|
|
@@ -65109,12 +65191,13 @@ var unknownHostingEvidence = (operation) => failure("external_outcome_unknown",
|
|
|
65109
65191
|
outcome: "unknown"
|
|
65110
65192
|
});
|
|
65111
65193
|
var readHostingAction = async (root, resolved, actionRef) => {
|
|
65112
|
-
|
|
65113
|
-
|
|
65194
|
+
const operation = resolved.request.operation;
|
|
65195
|
+
if (operation !== "hosting.pull_request" && operation !== "hosting.merge" || !resolved.marker)
|
|
65196
|
+
return unknownHostingEvidence(operation);
|
|
65114
65197
|
try {
|
|
65115
65198
|
const cfg = vcsConfig("load", root);
|
|
65116
65199
|
if (!cfg.ok || !cfg.tokenPath || !cfg.provider || cfg.provider !== "github" && cfg.provider !== "gitlab")
|
|
65117
|
-
return unknownHostingEvidence(
|
|
65200
|
+
return unknownHostingEvidence(operation);
|
|
65118
65201
|
const remote = pushRemote(root);
|
|
65119
65202
|
const repo = remote ? remoteRepo(remote) : null;
|
|
65120
65203
|
const approved = resolved.descriptorPayload;
|
|
@@ -65133,7 +65216,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
65133
65216
|
}
|
|
65134
65217
|
})();
|
|
65135
65218
|
if (!repo || typeof branch !== "string" || typeof target !== "string" || typeof sourceCommit !== "string" || typeof approvedRemote !== "string" || !currentIdentity || currentIdentity !== approvedIdentity || repo !== remoteRepo(approvedRemote) || remoteProvider2(approvedRemote) !== null && remoteProvider2(approvedRemote) !== cfg.provider || remoteParts(approvedRemote)?.host !== apiHost)
|
|
65136
|
-
return unknownHostingEvidence(
|
|
65219
|
+
return unknownHostingEvidence(operation);
|
|
65137
65220
|
const token = fs14.readFileSync(cfg.tokenPath, "utf8").trim();
|
|
65138
65221
|
const headers = { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json" };
|
|
65139
65222
|
const url = cfg.provider === "github" ? `https://api.github.com/repos/${repo}/pulls?state=all&head=${encodeURIComponent(`${repo.split("/")[0]}:${branch}`)}&base=${encodeURIComponent(target)}&per_page=100&page=1` : `${cfg.gitlab?.apiUrl ?? "https://gitlab.com/api/v4"}/projects/${encodeURIComponent(repo)}/merge_requests?state=all&source_branch=${encodeURIComponent(branch)}&target_branch=${encodeURIComponent(target)}&per_page=100&page=1`;
|
|
@@ -65141,14 +65224,14 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
65141
65224
|
headers: cfg.provider === "gitlab" ? { "PRIVATE-TOKEN": token } : headers
|
|
65142
65225
|
});
|
|
65143
65226
|
if (!response.ok)
|
|
65144
|
-
return unknownHostingEvidence(
|
|
65227
|
+
return unknownHostingEvidence(operation);
|
|
65145
65228
|
const values = await response.json();
|
|
65146
65229
|
if (!Array.isArray(values) || values.length >= 100)
|
|
65147
|
-
return unknownHostingEvidence(
|
|
65230
|
+
return unknownHostingEvidence(operation);
|
|
65148
65231
|
const records = [];
|
|
65149
65232
|
for (const item of values) {
|
|
65150
65233
|
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
65151
|
-
return unknownHostingEvidence(
|
|
65234
|
+
return unknownHostingEvidence(operation);
|
|
65152
65235
|
const record = item;
|
|
65153
65236
|
const body = record.body ?? record.description;
|
|
65154
65237
|
const base = record.base;
|
|
@@ -65160,7 +65243,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
65160
65243
|
const shaValue = cfg.provider === "github" && githubShape ? head.sha : record.sha;
|
|
65161
65244
|
const idValue = record.number ?? record.iid ?? record.id;
|
|
65162
65245
|
if (body !== undefined && body !== null && typeof body !== "string" || (cfg.provider === "github" ? !githubShape : !gitlabShape) || typeof targetValue !== "string" || typeof sourceValue !== "string" || typeof shaValue !== "string" || typeof idValue !== "string" && typeof idValue !== "number")
|
|
65163
|
-
return unknownHostingEvidence(
|
|
65246
|
+
return unknownHostingEvidence(operation);
|
|
65164
65247
|
records.push(record);
|
|
65165
65248
|
}
|
|
65166
65249
|
const matches = records.filter((item) => {
|
|
@@ -65170,15 +65253,23 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
65170
65253
|
const itemTarget = cfg.provider === "github" ? base?.ref : item.target_branch;
|
|
65171
65254
|
const itemSource = cfg.provider === "github" ? head?.ref : item.source_branch;
|
|
65172
65255
|
const itemSha = cfg.provider === "github" ? head?.sha : item.sha;
|
|
65173
|
-
|
|
65256
|
+
const merged = cfg.provider === "github" ? typeof item.merged_at === "string" : item.state === "merged" || typeof item.merged_at === "string";
|
|
65257
|
+
return (operation === "hosting.merge" ? merged : body.includes(resolved.marker)) && itemTarget === target && itemSource === branch && itemSha === sourceCommit;
|
|
65174
65258
|
});
|
|
65259
|
+
if (operation === "hosting.merge" && records.some((item) => {
|
|
65260
|
+
const base = item.base;
|
|
65261
|
+
const head = item.head;
|
|
65262
|
+
return (cfg.provider === "github" ? base?.ref : item.target_branch) === target && (cfg.provider === "github" ? head?.ref : item.source_branch) === branch && (cfg.provider === "github" ? typeof item.merged_at !== "string" : item.state !== "merged" && typeof item.merged_at !== "string");
|
|
65263
|
+
}))
|
|
65264
|
+
return unknownHostingEvidence(operation);
|
|
65175
65265
|
if (matches.length !== 1)
|
|
65176
|
-
return unknownHostingEvidence(
|
|
65266
|
+
return unknownHostingEvidence(operation);
|
|
65177
65267
|
const item = matches[0];
|
|
65178
65268
|
const id = item.number ?? item.iid ?? item.id;
|
|
65179
65269
|
if (typeof id !== "string" && typeof id !== "number")
|
|
65180
|
-
return unknownHostingEvidence(
|
|
65270
|
+
return unknownHostingEvidence(operation);
|
|
65181
65271
|
const evidenceDigest = sha256({
|
|
65272
|
+
operation,
|
|
65182
65273
|
provider: cfg.provider,
|
|
65183
65274
|
id,
|
|
65184
65275
|
marker: resolved.marker,
|
|
@@ -65199,7 +65290,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
65199
65290
|
data: { provider: cfg.provider, id }
|
|
65200
65291
|
});
|
|
65201
65292
|
} catch {
|
|
65202
|
-
return unknownHostingEvidence(
|
|
65293
|
+
return unknownHostingEvidence(operation);
|
|
65203
65294
|
}
|
|
65204
65295
|
};
|
|
65205
65296
|
var appendMarker = (text, marker) => `${text}${text ? `
|
|
@@ -65289,7 +65380,70 @@ var readYouTrackAction = async (root, resolved, actionRef) => {
|
|
|
65289
65380
|
return unknownHostingEvidence(operation);
|
|
65290
65381
|
}
|
|
65291
65382
|
};
|
|
65292
|
-
var
|
|
65383
|
+
var readLocalAction = async (root, resolved, actionRef) => {
|
|
65384
|
+
const operation = resolved.request.operation;
|
|
65385
|
+
const target = resolved.descriptorPayload.resolved;
|
|
65386
|
+
if (!target)
|
|
65387
|
+
return unknownHostingEvidence(operation);
|
|
65388
|
+
let observed = null;
|
|
65389
|
+
if (operation === "git.commit") {
|
|
65390
|
+
const commit = gitValue(root, ["rev-parse", "HEAD"]);
|
|
65391
|
+
const parent = commit ? gitValue(root, ["rev-parse", `${commit}^`]) : null;
|
|
65392
|
+
const message = commit ? gitValue(root, ["show", "-s", "--format=%B", commit]) : null;
|
|
65393
|
+
const diff = commit && parent ? gitRaw(root, ["diff", "--raw", "-z", "--no-ext-diff", "--no-textconv", parent, commit]) : null;
|
|
65394
|
+
if (commit && parent === target.head && message === resolved.request.payload.message && diff !== null && sha256(diff) === target.staged)
|
|
65395
|
+
observed = { commit, parent, message, staged: target.staged };
|
|
65396
|
+
} else if (operation === "git.push") {
|
|
65397
|
+
const branch = target.branch;
|
|
65398
|
+
const commit = target.commit;
|
|
65399
|
+
const approvedRemote = target.remote;
|
|
65400
|
+
const currentRemote = pushRemote(root);
|
|
65401
|
+
const currentIdentity = currentRemote ? credentialFreeRemote(currentRemote) : null;
|
|
65402
|
+
if (typeof branch === "string" && typeof commit === "string" && typeof approvedRemote === "string" && currentIdentity === approvedRemote) {
|
|
65403
|
+
const remote = run3(root, ["ls-remote", currentRemote, `refs/heads/${branch}`]);
|
|
65404
|
+
const remoteCommit = remote.exitCode === 0 ? remote.stdout.trim().split(/\s+/)[0] : null;
|
|
65405
|
+
if (remoteCommit === commit)
|
|
65406
|
+
observed = { branch, commit, remote: approvedRemote };
|
|
65407
|
+
}
|
|
65408
|
+
} else if (operation === "git.branch_setup") {
|
|
65409
|
+
const targetBranch = resolved.request.payload.target_branch;
|
|
65410
|
+
const sdd = resolved.request.payload.sdd_dir;
|
|
65411
|
+
if (resolved.request.payload.action === "reapply_stash" && typeof sdd === "string") {
|
|
65412
|
+
try {
|
|
65413
|
+
const manifest = JSON.parse(fs14.readFileSync(path28.join(sdd, "manifest.json"), "utf8"));
|
|
65414
|
+
const stashes = gitRaw(root, ["stash", "list", "--format=%H"]);
|
|
65415
|
+
if (typeof target.stash_commit === "string" && manifest.stash_ref === undefined && stashes !== null && !stashes.split(`
|
|
65416
|
+
`).includes(target.stash_commit))
|
|
65417
|
+
observed = { stash_commit: target.stash_commit };
|
|
65418
|
+
} catch {}
|
|
65419
|
+
} else if (typeof targetBranch === "string" && typeof sdd === "string" && gitValue(root, ["branch", "--show-current"]) === targetBranch && typeof target.target_head === "string" && gitValue(root, ["rev-parse", "HEAD"]) === target.target_head) {
|
|
65420
|
+
try {
|
|
65421
|
+
const manifest = JSON.parse(fs14.readFileSync(path28.join(sdd, "manifest.json"), "utf8"));
|
|
65422
|
+
if (manifest.branch === targetBranch)
|
|
65423
|
+
observed = { branch: targetBranch };
|
|
65424
|
+
} catch {}
|
|
65425
|
+
}
|
|
65426
|
+
} else if (operation === "changelog.apply") {
|
|
65427
|
+
const afterDigest = target.afterDigest;
|
|
65428
|
+
const preview = changelogApplyPreview({ ...resolved.request.payload, workspace_root: root });
|
|
65429
|
+
if (!preview.error && typeof afterDigest === "string" && preview.beforeDigest === afterDigest)
|
|
65430
|
+
observed = { target: target.target, afterDigest };
|
|
65431
|
+
}
|
|
65432
|
+
if (!observed)
|
|
65433
|
+
return unknownHostingEvidence(operation);
|
|
65434
|
+
const evidenceDigest = sha256({ operation, observed });
|
|
65435
|
+
return success(null, null, {
|
|
65436
|
+
outcome: "succeeded",
|
|
65437
|
+
evidenceDigest,
|
|
65438
|
+
observation: {
|
|
65439
|
+
kind: "provider_read",
|
|
65440
|
+
actionRef,
|
|
65441
|
+
outcome: "succeeded",
|
|
65442
|
+
evidenceDigest
|
|
65443
|
+
}
|
|
65444
|
+
});
|
|
65445
|
+
};
|
|
65446
|
+
var readExternalAction = async (root, resolved, actionRef) => resolved.request.operation === "git.branch_setup" || resolved.request.operation === "git.commit" || resolved.request.operation === "git.push" || resolved.request.operation === "changelog.apply" ? readLocalAction(root, resolved, actionRef) : resolved.request.operation === "hosting.pull_request" || resolved.request.operation === "hosting.merge" ? readHostingAction(root, resolved, actionRef) : readYouTrackAction(root, resolved, actionRef);
|
|
65293
65447
|
var youTrackBase = () => {
|
|
65294
65448
|
try {
|
|
65295
65449
|
const config = JSON.parse(fs14.readFileSync(youTrackConfigPath(), "utf8"));
|
|
@@ -65347,6 +65501,16 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
65347
65501
|
return failure("storage_error", "current Git commit could not be resolved");
|
|
65348
65502
|
const dirty = Boolean((gitRaw(root, ["status", "--short"]) ?? "").trim());
|
|
65349
65503
|
const creation = action === "reapply_stash" || !target_branch ? { base_branch: policy.defaultTargetBranch } : branchCreationBaseline(root, target_branch, policy.defaultTargetBranch);
|
|
65504
|
+
let stashTarget;
|
|
65505
|
+
if (action === "reapply_stash") {
|
|
65506
|
+
try {
|
|
65507
|
+
const manifest = JSON.parse(fs14.readFileSync(path28.join(sdd_dir, "manifest.json"), "utf8"));
|
|
65508
|
+
const stash_ref = typeof manifest.stash_ref === "string" ? manifest.stash_ref : "";
|
|
65509
|
+
const stash_commit = stash_ref ? gitValue(root, ["rev-parse", stash_ref]) : null;
|
|
65510
|
+
if (stash_ref && stash_commit)
|
|
65511
|
+
stashTarget = { stash_ref, stash_commit };
|
|
65512
|
+
} catch {}
|
|
65513
|
+
}
|
|
65350
65514
|
const normalized = {
|
|
65351
65515
|
operation: request.operation,
|
|
65352
65516
|
payload: {
|
|
@@ -65361,7 +65525,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
65361
65525
|
request: normalized,
|
|
65362
65526
|
descriptorPayload: {
|
|
65363
65527
|
...normalized.payload,
|
|
65364
|
-
resolved: { head, dirty, ...creation }
|
|
65528
|
+
resolved: { head, dirty, ...creation, ...stashTarget }
|
|
65365
65529
|
}
|
|
65366
65530
|
});
|
|
65367
65531
|
}
|
|
@@ -65513,7 +65677,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
65513
65677
|
outcome: "not_started",
|
|
65514
65678
|
fields: [{ path: "source_branch", reason: "protected branch" }]
|
|
65515
65679
|
});
|
|
65516
|
-
const source_commit = gitValue(root, ["rev-parse", "
|
|
65680
|
+
const source_commit = source_branch ? gitValue(root, ["rev-parse", "--verify", `refs/heads/${source_branch}`]) : null;
|
|
65517
65681
|
const remote = pushRemote(root);
|
|
65518
65682
|
const identity = remote ? credentialFreeRemote(remote) : null;
|
|
65519
65683
|
if (!source_branch || !source_commit || !identity || remoteProvider2(identity) !== null && remoteProvider2(identity) !== String(vcsConfig("load", root).provider).toLowerCase())
|
|
@@ -65801,7 +65965,17 @@ var executeResolvedExternalAction = async (resolved, root, step, caller) => {
|
|
|
65801
65965
|
const workText = fresh.data.descriptorPayload.resolved?.workText;
|
|
65802
65966
|
const beforeDigest = typeof resolvedDetails.resolved?.beforeDigest === "string" ? resolvedDetails.resolved.beforeDigest : undefined;
|
|
65803
65967
|
const beforeExists = typeof resolvedDetails.resolved?.beforeExists === "boolean" ? resolvedDetails.resolved.beforeExists : undefined;
|
|
65804
|
-
|
|
65968
|
+
const result = await executeConcreteExternalAction(fresh.data.request, root, fresh.data.marker, resolvedDate, step, typeof workText === "string" ? workText : undefined, caller, beforeDigest, beforeExists);
|
|
65969
|
+
if (!result.ok && result.code === "external_outcome_unknown" && caller) {
|
|
65970
|
+
const observed = await readLocalAction(root, fresh.data, {
|
|
65971
|
+
kind: "host",
|
|
65972
|
+
host: caller.host,
|
|
65973
|
+
handle: `local-recovery:${fresh.data.request.operation}`
|
|
65974
|
+
});
|
|
65975
|
+
if (observed.ok)
|
|
65976
|
+
return success(null, null, { recovered: true });
|
|
65977
|
+
}
|
|
65978
|
+
return result;
|
|
65805
65979
|
};
|
|
65806
65980
|
|
|
65807
65981
|
// packages/workit-cli/src/task.ts
|
|
@@ -66361,7 +66535,7 @@ async function runActionCommand(argv, deps = {}) {
|
|
|
66361
66535
|
const task = tasks[0];
|
|
66362
66536
|
const prior = externalActionState(store, "workit_cli", actor, descriptor);
|
|
66363
66537
|
const priorRequest = priorExternalAction(store, "workit_cli", actor, normalized.operation, normalized.payload);
|
|
66364
|
-
if (priorRequest.ok && priorRequest.data.entry.data.consumption !== null && priorRequest.data.entry.data.consumption.state !== "uncertain") {
|
|
66538
|
+
if (priorRequest.ok && priorRequest.data.entry.data.consumption !== null && priorRequest.data.entry.data.consumption.state !== "uncertain" && normalized.operation !== "git.commit" && normalized.operation !== "git.push") {
|
|
66365
66539
|
const result = failure("permission_denied", "external action was already settled");
|
|
66366
66540
|
if (json)
|
|
66367
66541
|
jsonResult(outOf(deps), result);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
6
6
|
"keywords": [
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"typecheck": "tsc --noEmit"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@brainervirus/workit-core": "^1.2.
|
|
41
|
-
"@brainervirus/workit-cursor": "^1.2.
|
|
42
|
-
"@brainervirus/workit-opencode": "^1.2.
|
|
40
|
+
"@brainervirus/workit-core": "^1.2.1",
|
|
41
|
+
"@brainervirus/workit-cursor": "^1.2.1",
|
|
42
|
+
"@brainervirus/workit-opencode": "^1.2.1",
|
|
43
43
|
"@inkjs/ui": "2.0.0",
|
|
44
44
|
"ink": "7.1.1",
|
|
45
45
|
"react": "19.2.8"
|