@brainervirus/workit-codex 1.0.12 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/dist/launch-mcp.js +1701 -1517
- package/dist/workit-hook.js +422 -55
- package/package.json +3 -3
package/dist/workit-hook.js
CHANGED
|
@@ -3,8 +3,8 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// packages/workit-codex/hooks/workit-hook.ts
|
|
6
|
-
import { existsSync as
|
|
7
|
-
import
|
|
6
|
+
import { existsSync as existsSync5, realpathSync as realpathSync6, statSync } from "node:fs";
|
|
7
|
+
import path9 from "node:path";
|
|
8
8
|
|
|
9
9
|
// packages/workit-core/src/core/task-contract.ts
|
|
10
10
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -7353,7 +7353,13 @@ var refSchema = discriminatedUnion("kind", [
|
|
|
7353
7353
|
id
|
|
7354
7354
|
}).strict(),
|
|
7355
7355
|
object({ kind: literal("host"), host: hostSchema, handle: nonEmpty }).strict(),
|
|
7356
|
-
object({ kind: literal("external"), url: text.url() }).strict()
|
|
7356
|
+
object({ kind: literal("external"), url: text.url() }).strict(),
|
|
7357
|
+
object({
|
|
7358
|
+
kind: literal("standing"),
|
|
7359
|
+
workspace: nonEmpty,
|
|
7360
|
+
class: nonEmpty,
|
|
7361
|
+
configDigest: nullableDigest
|
|
7362
|
+
}).strict()
|
|
7357
7363
|
]);
|
|
7358
7364
|
var rewriteUnknown = (value, map) => {
|
|
7359
7365
|
if (Array.isArray(value)) {
|
|
@@ -7658,7 +7664,8 @@ var decisionSchema = object({
|
|
|
7658
7664
|
approvedContent: text,
|
|
7659
7665
|
displayed: text.optional(),
|
|
7660
7666
|
contentRefs: array(refSchema),
|
|
7661
|
-
statedChoice: object({ ref: text, text }).strict().optional()
|
|
7667
|
+
statedChoice: object({ ref: text, text }).strict().optional(),
|
|
7668
|
+
standing: object({ workspace: nonEmpty, class: nonEmpty }).strict().optional()
|
|
7662
7669
|
}).strict(),
|
|
7663
7670
|
digest,
|
|
7664
7671
|
response: _enum(["approved", "rejected", "stated"]),
|
|
@@ -9673,15 +9680,362 @@ function assertProductWriteAllowed(input) {
|
|
|
9673
9680
|
}
|
|
9674
9681
|
|
|
9675
9682
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
9676
|
-
import { createHash as
|
|
9683
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
9677
9684
|
import * as fs6 from "node:fs";
|
|
9678
9685
|
import { spawnSync } from "node:child_process";
|
|
9679
|
-
import
|
|
9686
|
+
import path8 from "node:path";
|
|
9680
9687
|
|
|
9681
9688
|
// packages/workit-core/src/core/authority.ts
|
|
9682
|
-
import { createHash as
|
|
9689
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
9683
9690
|
import * as fs5 from "node:fs";
|
|
9691
|
+
import path7 from "node:path";
|
|
9692
|
+
|
|
9693
|
+
// packages/workit-core/src/core/auto-approval.ts
|
|
9694
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
9695
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
9696
|
+
|
|
9697
|
+
// packages/workit-core/src/core/workspaces.ts
|
|
9698
|
+
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
9699
|
+
import path6 from "node:path";
|
|
9700
|
+
|
|
9701
|
+
// packages/workit-core/src/core/config.ts
|
|
9702
|
+
import {
|
|
9703
|
+
copyFileSync,
|
|
9704
|
+
cpSync,
|
|
9705
|
+
existsSync as existsSync3,
|
|
9706
|
+
mkdirSync as mkdirSync2,
|
|
9707
|
+
readFileSync as readFileSync2,
|
|
9708
|
+
readdirSync as readdirSync2,
|
|
9709
|
+
writeFileSync as writeFileSync2
|
|
9710
|
+
} from "node:fs";
|
|
9711
|
+
import os from "node:os";
|
|
9684
9712
|
import path5 from "node:path";
|
|
9713
|
+
|
|
9714
|
+
// packages/workit-core/src/core/boundary.ts
|
|
9715
|
+
var EVENT = {
|
|
9716
|
+
initialization: "initialization",
|
|
9717
|
+
provenance: "provenance",
|
|
9718
|
+
assets: "assets",
|
|
9719
|
+
configurationSource: "configuration_source",
|
|
9720
|
+
hooks: "hooks",
|
|
9721
|
+
mcpConnection: "mcp_connection",
|
|
9722
|
+
migration: "migration",
|
|
9723
|
+
installSteps: "install_steps",
|
|
9724
|
+
uncaughtFailure: "uncaught_failure",
|
|
9725
|
+
toolsFailed: "tools_failed",
|
|
9726
|
+
doctor: "doctor"
|
|
9727
|
+
};
|
|
9728
|
+
var errorDetail = (err) => {
|
|
9729
|
+
if (err instanceof Error) {
|
|
9730
|
+
return { error_name: err.name, error: err.message };
|
|
9731
|
+
}
|
|
9732
|
+
return { error_name: "unknown", error: String(err) };
|
|
9733
|
+
};
|
|
9734
|
+
|
|
9735
|
+
// packages/workit-core/src/core/config.ts
|
|
9736
|
+
var diagnosticLogger;
|
|
9737
|
+
var PRESETS = {
|
|
9738
|
+
gitflow: {
|
|
9739
|
+
allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
|
|
9740
|
+
protected: ["main", "develop", "master", "prod", "production"]
|
|
9741
|
+
},
|
|
9742
|
+
"github-flow": { allowed: ["*"], protected: ["main"] },
|
|
9743
|
+
"trunk-based": { allowed: ["*"], protected: ["main"] },
|
|
9744
|
+
custom: { allowed: [], protected: [] }
|
|
9745
|
+
};
|
|
9746
|
+
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path5.join(process.env.XDG_CONFIG_HOME || path5.join(os.homedir(), ".config"), "workit");
|
|
9747
|
+
var migratedDir = null;
|
|
9748
|
+
var migrationFailed = false;
|
|
9749
|
+
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
9750
|
+
if (migratedDir === dir)
|
|
9751
|
+
return dir;
|
|
9752
|
+
if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
|
|
9753
|
+
migratedDir = dir;
|
|
9754
|
+
return dir;
|
|
9755
|
+
}
|
|
9756
|
+
const legacy = path5.join(process.env.XDG_CONFIG_HOME || path5.join(os.homedir(), ".config"), "workflow-toolkit");
|
|
9757
|
+
if (!existsSync3(legacy)) {
|
|
9758
|
+
migratedDir = dir;
|
|
9759
|
+
return dir;
|
|
9760
|
+
}
|
|
9761
|
+
if (!migrationFailed && existsSync3(dir)) {
|
|
9762
|
+
migratedDir = dir;
|
|
9763
|
+
return dir;
|
|
9764
|
+
}
|
|
9765
|
+
migrationFailed = false;
|
|
9766
|
+
mkdirSync2(dir, { recursive: true });
|
|
9767
|
+
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
9768
|
+
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
9769
|
+
const src = path5.join(legacy, entry.name);
|
|
9770
|
+
const dest = path5.join(dir, entry.name);
|
|
9771
|
+
if (existsSync3(dest))
|
|
9772
|
+
continue;
|
|
9773
|
+
try {
|
|
9774
|
+
if (entry.isDirectory())
|
|
9775
|
+
cpSync(src, dest, { recursive: true });
|
|
9776
|
+
else if (entry.isFile())
|
|
9777
|
+
copyFileSync(src, dest);
|
|
9778
|
+
} catch (err) {
|
|
9779
|
+
migrationFailed = true;
|
|
9780
|
+
diagnosticLogger?.warn(EVENT.migration, { from: src, ok: false, ...errorDetail(err) });
|
|
9781
|
+
console.warn(`[workit] config migration: a file could not be copied to ${dir}; it will be retried on the next run`);
|
|
9782
|
+
}
|
|
9783
|
+
}
|
|
9784
|
+
if (!migrationFailed)
|
|
9785
|
+
migratedDir = dir;
|
|
9786
|
+
return dir;
|
|
9787
|
+
};
|
|
9788
|
+
var configDir = () => ensureConfigDir();
|
|
9789
|
+
var DEFAULTS = {
|
|
9790
|
+
locale: "en",
|
|
9791
|
+
localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
|
|
9792
|
+
timezone: "America/Santiago",
|
|
9793
|
+
branchPolicy: {
|
|
9794
|
+
preset: "gitflow",
|
|
9795
|
+
allowed: [...PRESETS.gitflow.allowed],
|
|
9796
|
+
protected: [...PRESETS.gitflow.protected]
|
|
9797
|
+
},
|
|
9798
|
+
commitPolicy: { preset: "conventional" }
|
|
9799
|
+
};
|
|
9800
|
+
var isConfigObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9801
|
+
|
|
9802
|
+
// packages/workit-core/src/core/workspaces.ts
|
|
9803
|
+
var readWorkspacesResult = (dir = configDir()) => {
|
|
9804
|
+
const file = path6.join(dir, "workspaces.json");
|
|
9805
|
+
let raw;
|
|
9806
|
+
try {
|
|
9807
|
+
raw = readFileSync3(file, "utf8");
|
|
9808
|
+
} catch {
|
|
9809
|
+
return { status: "missing", path: file, entries: [] };
|
|
9810
|
+
}
|
|
9811
|
+
let parsed;
|
|
9812
|
+
try {
|
|
9813
|
+
parsed = JSON.parse(raw);
|
|
9814
|
+
} catch {
|
|
9815
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not valid JSON` };
|
|
9816
|
+
}
|
|
9817
|
+
if (!isConfigObject(parsed)) {
|
|
9818
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
9819
|
+
}
|
|
9820
|
+
const list = parsed.workspaces;
|
|
9821
|
+
return {
|
|
9822
|
+
status: "valid",
|
|
9823
|
+
path: file,
|
|
9824
|
+
entries: Array.isArray(list) ? list : []
|
|
9825
|
+
};
|
|
9826
|
+
};
|
|
9827
|
+
var loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries;
|
|
9828
|
+
var globToRegExp = (glob) => {
|
|
9829
|
+
let out = "";
|
|
9830
|
+
for (let i = 0;i < glob.length; i++) {
|
|
9831
|
+
const c = glob[i];
|
|
9832
|
+
if (c === "*") {
|
|
9833
|
+
if (glob[i + 1] === "*") {
|
|
9834
|
+
if (glob[i + 2] === "/") {
|
|
9835
|
+
if (out === "")
|
|
9836
|
+
out += "/?";
|
|
9837
|
+
out += "(?:[^/]+/)*";
|
|
9838
|
+
i += 2;
|
|
9839
|
+
} else {
|
|
9840
|
+
if (i + 2 >= glob.length) {
|
|
9841
|
+
if (out.endsWith("/")) {
|
|
9842
|
+
out = out.slice(0, -1);
|
|
9843
|
+
out += "(?:/.*)?";
|
|
9844
|
+
} else {
|
|
9845
|
+
out += ".*";
|
|
9846
|
+
}
|
|
9847
|
+
} else {
|
|
9848
|
+
out += ".*";
|
|
9849
|
+
}
|
|
9850
|
+
i++;
|
|
9851
|
+
}
|
|
9852
|
+
} else {
|
|
9853
|
+
out += "[^/]*";
|
|
9854
|
+
}
|
|
9855
|
+
} else {
|
|
9856
|
+
out += c.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
9857
|
+
}
|
|
9858
|
+
}
|
|
9859
|
+
return new RegExp(`^${out}$`);
|
|
9860
|
+
};
|
|
9861
|
+
var matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/"));
|
|
9862
|
+
var realpathOf = (p) => {
|
|
9863
|
+
const real = realpathSync2.native ?? realpathSync2;
|
|
9864
|
+
try {
|
|
9865
|
+
return real(p);
|
|
9866
|
+
} catch {
|
|
9867
|
+
return p;
|
|
9868
|
+
}
|
|
9869
|
+
};
|
|
9870
|
+
var canonicalGlob = (glob) => {
|
|
9871
|
+
const m = /[*?[\]{]/.exec(glob);
|
|
9872
|
+
const prefix = m ? glob.slice(0, m.index) : glob;
|
|
9873
|
+
const rest = m ? glob.slice(m.index) : "";
|
|
9874
|
+
if (!prefix)
|
|
9875
|
+
return glob;
|
|
9876
|
+
const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
|
|
9877
|
+
return real + (prefix.endsWith("/") ? "/" : "") + rest;
|
|
9878
|
+
};
|
|
9879
|
+
var resolveWorkspaceFrom = (cwd, dir) => {
|
|
9880
|
+
const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
|
|
9881
|
+
for (const entry of loadWorkspacesFrom(dir)) {
|
|
9882
|
+
if (!entry || typeof entry !== "object")
|
|
9883
|
+
continue;
|
|
9884
|
+
const ws = entry;
|
|
9885
|
+
if (typeof ws.glob !== "string" || !ws.glob)
|
|
9886
|
+
continue;
|
|
9887
|
+
const glob = ws.glob.replaceAll("\\", "/");
|
|
9888
|
+
const canonical = canonicalGlob(glob);
|
|
9889
|
+
for (const target of targets) {
|
|
9890
|
+
if (matchWorkspace(glob, target))
|
|
9891
|
+
return ws;
|
|
9892
|
+
if (canonical !== glob && matchWorkspace(canonical, target))
|
|
9893
|
+
return ws;
|
|
9894
|
+
}
|
|
9895
|
+
}
|
|
9896
|
+
return null;
|
|
9897
|
+
};
|
|
9898
|
+
|
|
9899
|
+
// packages/workit-core/src/core/branch.ts
|
|
9900
|
+
import { execFileSync } from "node:child_process";
|
|
9901
|
+
var verifyPushIdentity = (root, provider, account) => {
|
|
9902
|
+
if (!account || typeof account !== "string")
|
|
9903
|
+
return { ok: false, error: "push identity requires a configured area account" };
|
|
9904
|
+
const probe = provider === "gitlab" ? ["api", "user", "--jq", ".username"] : ["api", "user", "--jq", ".login"];
|
|
9905
|
+
const bin = provider === "gitlab" ? "glab" : "gh";
|
|
9906
|
+
let login;
|
|
9907
|
+
try {
|
|
9908
|
+
login = execFileSync(bin, [...probe], {
|
|
9909
|
+
cwd: root,
|
|
9910
|
+
encoding: "utf8",
|
|
9911
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9912
|
+
}).trim();
|
|
9913
|
+
} catch {
|
|
9914
|
+
return { ok: false, error: `${bin} identity could not be resolved` };
|
|
9915
|
+
}
|
|
9916
|
+
if (!login || login.toLowerCase() !== account.toLowerCase())
|
|
9917
|
+
return {
|
|
9918
|
+
ok: false,
|
|
9919
|
+
error: `push identity ${login || "(unknown)"} does not match area account ${account}`
|
|
9920
|
+
};
|
|
9921
|
+
return { ok: true };
|
|
9922
|
+
};
|
|
9923
|
+
|
|
9924
|
+
// packages/workit-core/src/core/auto-approval.ts
|
|
9925
|
+
var AUTO_CLASSES = ["branch", "commit", "push", "pr", "merge"];
|
|
9926
|
+
var OPERATION_CLASS = {
|
|
9927
|
+
"git.branch_setup": "branch",
|
|
9928
|
+
"git.commit": "commit",
|
|
9929
|
+
"git.push": "push",
|
|
9930
|
+
"hosting.pull_request": "pr",
|
|
9931
|
+
"hosting.merge": "merge"
|
|
9932
|
+
};
|
|
9933
|
+
var operationAutoClass = (operation) => typeof operation === "string" ? OPERATION_CLASS[operation] ?? null : null;
|
|
9934
|
+
var digestOf = (configDirPath) => {
|
|
9935
|
+
try {
|
|
9936
|
+
const raw = readFileSync4(`${configDirPath}/workspaces.json`, "utf8");
|
|
9937
|
+
return createHash3("sha256").update(raw).digest("hex");
|
|
9938
|
+
} catch {
|
|
9939
|
+
return null;
|
|
9940
|
+
}
|
|
9941
|
+
};
|
|
9942
|
+
var normalizeClasses = (value) => {
|
|
9943
|
+
if (value === true)
|
|
9944
|
+
return [...AUTO_CLASSES];
|
|
9945
|
+
if (!Array.isArray(value))
|
|
9946
|
+
return [];
|
|
9947
|
+
const seen = new Set;
|
|
9948
|
+
for (const item of value) {
|
|
9949
|
+
if (typeof item === "string" && AUTO_CLASSES.includes(item))
|
|
9950
|
+
seen.add(item);
|
|
9951
|
+
}
|
|
9952
|
+
return [...seen];
|
|
9953
|
+
};
|
|
9954
|
+
var resolveAutoApproval = (root, dir = configDir()) => {
|
|
9955
|
+
let match;
|
|
9956
|
+
try {
|
|
9957
|
+
match = resolveWorkspaceFrom(root, dir);
|
|
9958
|
+
} catch {
|
|
9959
|
+
return { status: "off" };
|
|
9960
|
+
}
|
|
9961
|
+
if (!match)
|
|
9962
|
+
return { status: "off" };
|
|
9963
|
+
const classes = normalizeClasses(match.autoApprove);
|
|
9964
|
+
if (classes.length === 0)
|
|
9965
|
+
return { status: "off" };
|
|
9966
|
+
return {
|
|
9967
|
+
status: "on",
|
|
9968
|
+
workspace: match.name,
|
|
9969
|
+
classes,
|
|
9970
|
+
account: typeof match.vcs?.account === "string" && match.vcs.account ? match.vcs.account : null,
|
|
9971
|
+
configDigest: digestOf(dir)
|
|
9972
|
+
};
|
|
9973
|
+
};
|
|
9974
|
+
var isStandingReceipt = (value) => typeof value === "object" && value !== null && value.kind === "standing";
|
|
9975
|
+
var standingApprovalLive = (root, receipt, cls, dir) => {
|
|
9976
|
+
if (!isStandingReceipt(receipt))
|
|
9977
|
+
return false;
|
|
9978
|
+
if (typeof cls !== "string" || !AUTO_CLASSES.includes(cls))
|
|
9979
|
+
return false;
|
|
9980
|
+
if (receipt.class !== cls)
|
|
9981
|
+
return false;
|
|
9982
|
+
const resolved = resolveAutoApproval(root, dir);
|
|
9983
|
+
if (resolved.status !== "on")
|
|
9984
|
+
return false;
|
|
9985
|
+
if (resolved.workspace !== receipt.workspace)
|
|
9986
|
+
return false;
|
|
9987
|
+
return resolved.classes.includes(cls);
|
|
9988
|
+
};
|
|
9989
|
+
var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
9990
|
+
const resolved = resolveAutoApproval(root, dir);
|
|
9991
|
+
return {
|
|
9992
|
+
kind: "standing",
|
|
9993
|
+
workspace,
|
|
9994
|
+
class: cls,
|
|
9995
|
+
configDigest: resolved.status === "on" && resolved.workspace === workspace ? resolved.configDigest : null
|
|
9996
|
+
};
|
|
9997
|
+
};
|
|
9998
|
+
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
9999
|
+
if (task.status !== "active")
|
|
10000
|
+
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
10001
|
+
const session = task.intent?.provenance?.session;
|
|
10002
|
+
if (!session || session.kind !== "host" || session.host !== caller.host || session.handle !== caller.actor)
|
|
10003
|
+
return failure("permission_denied", "standing approval requires the lead session");
|
|
10004
|
+
const standing = binding.standing;
|
|
10005
|
+
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
10006
|
+
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
10007
|
+
let operation;
|
|
10008
|
+
try {
|
|
10009
|
+
operation = typeof binding.approvedContent === "string" ? JSON.parse(binding.approvedContent).operation : null;
|
|
10010
|
+
} catch {
|
|
10011
|
+
operation = null;
|
|
10012
|
+
}
|
|
10013
|
+
const cls = operationAutoClass(operation);
|
|
10014
|
+
if (!cls || cls !== standing.class)
|
|
10015
|
+
return failure("permission_denied", "standing approval does not cover this operation");
|
|
10016
|
+
const resolved = resolveAutoApproval(root);
|
|
10017
|
+
if (resolved.status !== "on" || resolved.workspace !== standing.workspace || !resolved.classes.includes(cls))
|
|
10018
|
+
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
10019
|
+
if (cls === "push") {
|
|
10020
|
+
const workspace = resolveWorkspaceFrom(root, configDir());
|
|
10021
|
+
const provider = workspace?.vcs?.provider;
|
|
10022
|
+
const account = resolved.account;
|
|
10023
|
+
if (!provider || !account)
|
|
10024
|
+
return failure("permission_denied", "auto-push requires a workspace provider and area account");
|
|
10025
|
+
const identity = verifyPushIdentity(root, provider, account);
|
|
10026
|
+
if (!identity.ok)
|
|
10027
|
+
return failure("permission_denied", identity.error);
|
|
10028
|
+
}
|
|
10029
|
+
return success(null, null, {
|
|
10030
|
+
kind: "host_observed",
|
|
10031
|
+
host: caller.host,
|
|
10032
|
+
session: { kind: "host", host: caller.host, handle: caller.actor },
|
|
10033
|
+
workerId: null,
|
|
10034
|
+
receipts: [standingReceiptFor(root, standing.workspace, cls)]
|
|
10035
|
+
});
|
|
10036
|
+
};
|
|
10037
|
+
|
|
10038
|
+
// packages/workit-core/src/core/authority.ts
|
|
9685
10039
|
var verifiedAuthorities = new WeakMap;
|
|
9686
10040
|
var trustedAuthority = (kind, provenance, expected, binding, caller) => {
|
|
9687
10041
|
const token = {};
|
|
@@ -9828,7 +10182,7 @@ function applicableDecision(task, purpose, binding, checkoutRoot) {
|
|
|
9828
10182
|
return [];
|
|
9829
10183
|
return task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, entry.data.binding).ok : entry.data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map((entry) => entry.data);
|
|
9830
10184
|
}
|
|
9831
|
-
var digestBytes2 = (bytes) =>
|
|
10185
|
+
var digestBytes2 = (bytes) => createHash4("sha256").update(bytes).digest("hex");
|
|
9832
10186
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
9833
10187
|
let root;
|
|
9834
10188
|
try {
|
|
@@ -9841,23 +10195,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
9841
10195
|
continue;
|
|
9842
10196
|
if (!reference.digest)
|
|
9843
10197
|
return failure("invalid_input", "document references require a byte digest");
|
|
9844
|
-
const target =
|
|
9845
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
10198
|
+
const target = path7.resolve(root, reference.path);
|
|
10199
|
+
if (target !== root && !target.startsWith(`${root}${path7.sep}`))
|
|
9846
10200
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
9847
10201
|
fields: [
|
|
9848
10202
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
9849
10203
|
]
|
|
9850
10204
|
});
|
|
9851
10205
|
try {
|
|
9852
|
-
const relative =
|
|
10206
|
+
const relative = path7.relative(root, target);
|
|
9853
10207
|
let current = root;
|
|
9854
|
-
for (const segment of relative.split(
|
|
10208
|
+
for (const segment of relative.split(path7.sep)) {
|
|
9855
10209
|
if (!segment)
|
|
9856
10210
|
continue;
|
|
9857
|
-
current =
|
|
10211
|
+
current = path7.join(current, segment);
|
|
9858
10212
|
const stat = fs5.lstatSync(current);
|
|
9859
10213
|
const real = fs5.realpathSync(current);
|
|
9860
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
10214
|
+
if (real !== root && !real.startsWith(`${root}${path7.sep}`))
|
|
9861
10215
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
9862
10216
|
fields: [
|
|
9863
10217
|
{
|
|
@@ -9910,6 +10264,20 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
9910
10264
|
return failure("permission_denied", "decision was rejected");
|
|
9911
10265
|
if (entry.provenance.kind !== "host_observed" || entry.provenance.receipts.length === 0 || !provenanceMatches(entry.provenance, authority.provenance))
|
|
9912
10266
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
10267
|
+
const receipts = entry.provenance.receipts;
|
|
10268
|
+
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
10269
|
+
const questionBacked = receipts.some((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "host");
|
|
10270
|
+
if (!questionBacked && standing.length > 0) {
|
|
10271
|
+
let operation;
|
|
10272
|
+
try {
|
|
10273
|
+
operation = JSON.parse(decision.binding.approvedContent).operation;
|
|
10274
|
+
} catch {
|
|
10275
|
+
operation = null;
|
|
10276
|
+
}
|
|
10277
|
+
const cls = operationAutoClass(operation);
|
|
10278
|
+
if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls)))
|
|
10279
|
+
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
10280
|
+
}
|
|
9913
10281
|
if (decision.revoked)
|
|
9914
10282
|
return failure("permission_denied", "decision is revoked");
|
|
9915
10283
|
if (decision.digest !== decisionDigest(decision))
|
|
@@ -10209,15 +10577,15 @@ var bindingCovers = scopeCovers;
|
|
|
10209
10577
|
var verifyDecisionContent = verifyContentRefs;
|
|
10210
10578
|
|
|
10211
10579
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
10212
|
-
var digestBytes3 = (value) =>
|
|
10213
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
10214
|
-
var relative = (root, candidate) =>
|
|
10580
|
+
var digestBytes3 = (value) => createHash5("sha256").update(value).digest("hex");
|
|
10581
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path8.sep}`);
|
|
10582
|
+
var relative = (root, candidate) => path8.relative(root, candidate).split(path8.sep).join("/") || ".";
|
|
10215
10583
|
var canonicalPath = (value) => {
|
|
10216
10584
|
if (typeof value !== "string" || !value)
|
|
10217
10585
|
return null;
|
|
10218
10586
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
10219
10587
|
return null;
|
|
10220
|
-
const normalized =
|
|
10588
|
+
const normalized = path8.posix.normalize(value);
|
|
10221
10589
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
10222
10590
|
return null;
|
|
10223
10591
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -10265,14 +10633,14 @@ var gitPaths = (root) => {
|
|
|
10265
10633
|
git: /not a git repository/i.test(stderr) ? false : true
|
|
10266
10634
|
};
|
|
10267
10635
|
}
|
|
10268
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
10636
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path8.sep).join("/")) : [];
|
|
10269
10637
|
const stagedDeleted = spawnSync("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
10270
10638
|
if (stagedDeleted.status !== 0) {
|
|
10271
10639
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
10272
10640
|
if (!/not a git repository/i.test(stderr))
|
|
10273
10641
|
return { paths, uncertain: true, git: true };
|
|
10274
10642
|
} else if (stagedDeleted.stdout) {
|
|
10275
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
10643
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path8.sep).join("/")));
|
|
10276
10644
|
}
|
|
10277
10645
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
10278
10646
|
};
|
|
@@ -10287,7 +10655,7 @@ var walk = (root, directory, output) => {
|
|
|
10287
10655
|
for (const name of names) {
|
|
10288
10656
|
if (name === ".git" || name === ".workit")
|
|
10289
10657
|
continue;
|
|
10290
|
-
const target =
|
|
10658
|
+
const target = path8.join(directory, name);
|
|
10291
10659
|
let stat;
|
|
10292
10660
|
try {
|
|
10293
10661
|
stat = fs6.lstatSync(target);
|
|
@@ -10305,12 +10673,12 @@ var walk = (root, directory, output) => {
|
|
|
10305
10673
|
var scopeRoots = (root, scope) => {
|
|
10306
10674
|
const roots = [];
|
|
10307
10675
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
10308
|
-
const target =
|
|
10676
|
+
const target = path8.resolve(root, item);
|
|
10309
10677
|
if (!inside(root, target))
|
|
10310
10678
|
return failure("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
|
|
10311
10679
|
let ancestor = target;
|
|
10312
10680
|
while (!fs6.existsSync(ancestor) && ancestor !== root)
|
|
10313
|
-
ancestor =
|
|
10681
|
+
ancestor = path8.dirname(ancestor);
|
|
10314
10682
|
try {
|
|
10315
10683
|
if (!inside(root, fs6.realpathSync(ancestor)))
|
|
10316
10684
|
return failure("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
|
|
@@ -10362,7 +10730,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
10362
10730
|
for (const item of [...names].sort()) {
|
|
10363
10731
|
if (!scopeMatches(item, normalizedScope))
|
|
10364
10732
|
continue;
|
|
10365
|
-
const target =
|
|
10733
|
+
const target = path8.join(checkout, item);
|
|
10366
10734
|
try {
|
|
10367
10735
|
const stat = fs6.lstatSync(target);
|
|
10368
10736
|
if (stat.isSymbolicLink()) {
|
|
@@ -10905,7 +11273,8 @@ var exportDigest = (bundle) => sha256({
|
|
|
10905
11273
|
// packages/workit-core/src/core/route-intent.ts
|
|
10906
11274
|
var GUIDANCE = {
|
|
10907
11275
|
"git.branch_setup": "use the Workit git.branch_setup action (workit_external_action in hosts, or `workit action git.branch_setup --payload '{}' --confirm --json` in the CLI)",
|
|
10908
|
-
"hosting.pull_request": "use the Workit hosting.pull_request action (workit_external_action in hosts, or `workit action hosting.pull_request --payload '{...}' --confirm --json` in the CLI), then drive the created PR through workit-babysit"
|
|
11276
|
+
"hosting.pull_request": "use the Workit hosting.pull_request action (workit_external_action in hosts, or `workit action hosting.pull_request --payload '{...}' --confirm --json` in the CLI), then drive the created PR through workit-babysit",
|
|
11277
|
+
"hosting.merge": "use the Workit hosting.merge action (workit_external_action in hosts, or `workit action hosting.merge --payload '{...}' --confirm --json` in the CLI)"
|
|
10909
11278
|
};
|
|
10910
11279
|
var unbalancedQuotes = (segment) => {
|
|
10911
11280
|
const text = segment.replace(/\\./gu, "");
|
|
@@ -10940,6 +11309,10 @@ function shellRouteIntent(command) {
|
|
|
10940
11309
|
return { route: "hosting.pull_request", guidance: GUIDANCE["hosting.pull_request"] };
|
|
10941
11310
|
if (head === "glab" && second === "mr" && third === "create")
|
|
10942
11311
|
return { route: "hosting.pull_request", guidance: GUIDANCE["hosting.pull_request"] };
|
|
11312
|
+
if (head === "gh" && second === "pr" && third === "merge")
|
|
11313
|
+
return { route: "hosting.merge", guidance: GUIDANCE["hosting.merge"] };
|
|
11314
|
+
if (head === "glab" && second === "mr" && third === "merge")
|
|
11315
|
+
return { route: "hosting.merge", guidance: GUIDANCE["hosting.merge"] };
|
|
10943
11316
|
}
|
|
10944
11317
|
return null;
|
|
10945
11318
|
}
|
|
@@ -10960,27 +11333,6 @@ function shouldDenyShellRoute(dir, command) {
|
|
|
10960
11333
|
return null;
|
|
10961
11334
|
return shellRouteIntent(command);
|
|
10962
11335
|
}
|
|
10963
|
-
// packages/workit-core/src/core/config.ts
|
|
10964
|
-
var PRESETS = {
|
|
10965
|
-
gitflow: {
|
|
10966
|
-
allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
|
|
10967
|
-
protected: ["main", "develop", "master", "prod", "production"]
|
|
10968
|
-
},
|
|
10969
|
-
"github-flow": { allowed: ["*"], protected: ["main"] },
|
|
10970
|
-
"trunk-based": { allowed: ["*"], protected: ["main"] },
|
|
10971
|
-
custom: { allowed: [], protected: [] }
|
|
10972
|
-
};
|
|
10973
|
-
var DEFAULTS = {
|
|
10974
|
-
locale: "en",
|
|
10975
|
-
localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
|
|
10976
|
-
timezone: "America/Santiago",
|
|
10977
|
-
branchPolicy: {
|
|
10978
|
-
preset: "gitflow",
|
|
10979
|
-
allowed: [...PRESETS.gitflow.allowed],
|
|
10980
|
-
protected: [...PRESETS.gitflow.protected]
|
|
10981
|
-
},
|
|
10982
|
-
commitPolicy: { preset: "conventional" }
|
|
10983
|
-
};
|
|
10984
11336
|
// packages/workit-core/src/core/external-action.ts
|
|
10985
11337
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
10986
11338
|
object({
|
|
@@ -11014,6 +11366,13 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
11014
11366
|
babysit: boolean2().optional()
|
|
11015
11367
|
}).strict()
|
|
11016
11368
|
}).strict(),
|
|
11369
|
+
object({
|
|
11370
|
+
operation: literal("hosting.merge"),
|
|
11371
|
+
payload: object({
|
|
11372
|
+
target_branch: string2().min(1).optional(),
|
|
11373
|
+
source_branch: string2().min(1).optional()
|
|
11374
|
+
}).strict()
|
|
11375
|
+
}).strict(),
|
|
11017
11376
|
object({
|
|
11018
11377
|
operation: literal("youtrack.update"),
|
|
11019
11378
|
payload: object({
|
|
@@ -11074,7 +11433,7 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
11074
11433
|
}).strict()
|
|
11075
11434
|
]);
|
|
11076
11435
|
// packages/workit-core/src/core/task-engine.ts
|
|
11077
|
-
import { realpathSync as
|
|
11436
|
+
import { realpathSync as realpathSync5 } from "node:fs";
|
|
11078
11437
|
|
|
11079
11438
|
// packages/workit-core/src/core/policy-resolver.ts
|
|
11080
11439
|
var resolverInputSchema = object({
|
|
@@ -11781,7 +12140,7 @@ class WorkitCore {
|
|
|
11781
12140
|
}
|
|
11782
12141
|
contextRootError() {
|
|
11783
12142
|
try {
|
|
11784
|
-
if (
|
|
12143
|
+
if (realpathSync5(this.context.root) !== this.store.root)
|
|
11785
12144
|
return failure("invalid_input", "operation context root does not match the task store root");
|
|
11786
12145
|
} catch {
|
|
11787
12146
|
return failure("invalid_input", "operation context root cannot be resolved");
|
|
@@ -12127,7 +12486,10 @@ class WorkitCore {
|
|
|
12127
12486
|
observeDecision(request, observation) {
|
|
12128
12487
|
return this.recordDecision(request, observation, true);
|
|
12129
12488
|
}
|
|
12130
|
-
|
|
12489
|
+
observeStandingDecision(request) {
|
|
12490
|
+
return this.recordDecision(request, undefined, true, true);
|
|
12491
|
+
}
|
|
12492
|
+
recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
|
|
12131
12493
|
const root = this.contextRootError();
|
|
12132
12494
|
if (!root.ok)
|
|
12133
12495
|
return root;
|
|
@@ -12142,8 +12504,10 @@ class WorkitCore {
|
|
|
12142
12504
|
return failure("permission_denied", "stated choices cannot authorize mutating actions");
|
|
12143
12505
|
if (stated && !input.binding?.statedChoice)
|
|
12144
12506
|
return failure("invalid_input", "stated choices require binding.statedChoice");
|
|
12145
|
-
if (nativeRequired && nativeObservation === undefined && !stated)
|
|
12507
|
+
if (nativeRequired && nativeObservation === undefined && !stated && !standing)
|
|
12146
12508
|
return failure("permission_denied", "native decision observation is required");
|
|
12509
|
+
if (standing && (!input.binding?.standing || input.purpose !== "action" || input.response !== "approved"))
|
|
12510
|
+
return failure("invalid_input", "standing approvals need an approved action binding with a standing rule");
|
|
12147
12511
|
const task = this.store.readTask(input.taskId);
|
|
12148
12512
|
if (!task.ok)
|
|
12149
12513
|
return task;
|
|
@@ -12175,7 +12539,10 @@ class WorkitCore {
|
|
|
12175
12539
|
consumption: null
|
|
12176
12540
|
};
|
|
12177
12541
|
const data = { ...base, digest: decisionDigest(base) };
|
|
12178
|
-
const
|
|
12542
|
+
const standingApproval = standing && !stated ? verifyStandingApproval(this.store.root, task.data, this.context.caller, input.binding) : null;
|
|
12543
|
+
if (standingApproval && !standingApproval.ok)
|
|
12544
|
+
return standingApproval;
|
|
12545
|
+
const native = nativeRequired && !stated && !standingApproval ? verifyNativeDecision(this.context.nativeAuthority, {
|
|
12179
12546
|
observation: nativeObservation,
|
|
12180
12547
|
expected: {
|
|
12181
12548
|
taskId: task.data.id,
|
|
@@ -12206,7 +12573,7 @@ class WorkitCore {
|
|
|
12206
12573
|
const entry = {
|
|
12207
12574
|
id: newId(),
|
|
12208
12575
|
recordedAt: mutation.now,
|
|
12209
|
-
provenance: nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
12576
|
+
provenance: standingApproval?.ok === true ? standingApproval.data : nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
12210
12577
|
data
|
|
12211
12578
|
};
|
|
12212
12579
|
return success(mutation.revision, null, {
|
|
@@ -13034,7 +13401,7 @@ class WorkitCore {
|
|
|
13034
13401
|
if (view.task.workspaceId !== view.workspace.id)
|
|
13035
13402
|
return failure("invalid_input", "task and workspace bindings are invalid");
|
|
13036
13403
|
try {
|
|
13037
|
-
if (
|
|
13404
|
+
if (realpathSync5(view.workspace.root) !== this.store.root)
|
|
13038
13405
|
return failure("invalid_input", "task view root does not match the task store");
|
|
13039
13406
|
} catch {
|
|
13040
13407
|
return failure("invalid_input", "task view root cannot be resolved");
|
|
@@ -13424,7 +13791,7 @@ var parseCodexHookInput = (value) => {
|
|
|
13424
13791
|
return { ok: false, error: "permission_mode is invalid" };
|
|
13425
13792
|
if (!(value.transcript_path === null || nonEmpty2(value.transcript_path)))
|
|
13426
13793
|
return { ok: false, error: "transcript_path must be a string or null" };
|
|
13427
|
-
if (!nonEmpty2(value.cwd) || !
|
|
13794
|
+
if (!nonEmpty2(value.cwd) || !path9.isAbsolute(value.cwd) || !existsSync5(value.cwd))
|
|
13428
13795
|
return { ok: false, error: "cwd must be an existing absolute path" };
|
|
13429
13796
|
try {
|
|
13430
13797
|
if (!statSync(value.cwd).isDirectory())
|
|
@@ -13450,7 +13817,7 @@ var parseCodexHookInput = (value) => {
|
|
|
13450
13817
|
model: value.model,
|
|
13451
13818
|
permission_mode: value.permission_mode,
|
|
13452
13819
|
transcript_path: value.transcript_path,
|
|
13453
|
-
cwd:
|
|
13820
|
+
cwd: realpathSync6(value.cwd),
|
|
13454
13821
|
...event === "SessionStart" ? { source: value.source } : {},
|
|
13455
13822
|
...nonEmpty2(value.turn_id) ? { turn_id: value.turn_id } : {},
|
|
13456
13823
|
...nonEmpty2(value.tool_name) ? { tool_name: value.tool_name } : {},
|