@brainervirus/workit-cursor 1.0.12 → 1.1.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/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +421 -84
- package/dist/mcp-server.js +1701 -1517
- package/dist/workit-hook.js +416 -54
- package/package.json +3 -3
|
@@ -7655,7 +7655,13 @@ var init_task_contract = __esm(() => {
|
|
|
7655
7655
|
id
|
|
7656
7656
|
}).strict(),
|
|
7657
7657
|
object({ kind: literal("host"), host: hostSchema, handle: nonEmpty }).strict(),
|
|
7658
|
-
object({ kind: literal("external"), url: text.url() }).strict()
|
|
7658
|
+
object({ kind: literal("external"), url: text.url() }).strict(),
|
|
7659
|
+
object({
|
|
7660
|
+
kind: literal("standing"),
|
|
7661
|
+
workspace: nonEmpty,
|
|
7662
|
+
class: nonEmpty,
|
|
7663
|
+
configDigest: nullableDigest
|
|
7664
|
+
}).strict()
|
|
7659
7665
|
]);
|
|
7660
7666
|
provenanceSchema = object({
|
|
7661
7667
|
kind: _enum(["host_observed", "agent_reported", "imported"]),
|
|
@@ -7876,7 +7882,8 @@ var init_task_contract = __esm(() => {
|
|
|
7876
7882
|
approvedContent: text,
|
|
7877
7883
|
displayed: text.optional(),
|
|
7878
7884
|
contentRefs: array(refSchema),
|
|
7879
|
-
statedChoice: object({ ref: text, text }).strict().optional()
|
|
7885
|
+
statedChoice: object({ ref: text, text }).strict().optional(),
|
|
7886
|
+
standing: object({ workspace: nonEmpty, class: nonEmpty }).strict().optional()
|
|
7880
7887
|
}).strict(),
|
|
7881
7888
|
digest,
|
|
7882
7889
|
response: _enum(["approved", "rejected", "stated"]),
|
|
@@ -9794,10 +9801,360 @@ var init_workers = __esm(() => {
|
|
|
9794
9801
|
UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
|
|
9795
9802
|
});
|
|
9796
9803
|
|
|
9797
|
-
// packages/workit-core/src/core/
|
|
9804
|
+
// packages/workit-core/src/core/boundary.ts
|
|
9805
|
+
var EVENT, errorDetail = (err) => {
|
|
9806
|
+
if (err instanceof Error) {
|
|
9807
|
+
return { error_name: err.name, error: err.message };
|
|
9808
|
+
}
|
|
9809
|
+
return { error_name: "unknown", error: String(err) };
|
|
9810
|
+
};
|
|
9811
|
+
var init_boundary = __esm(() => {
|
|
9812
|
+
EVENT = {
|
|
9813
|
+
initialization: "initialization",
|
|
9814
|
+
provenance: "provenance",
|
|
9815
|
+
assets: "assets",
|
|
9816
|
+
configurationSource: "configuration_source",
|
|
9817
|
+
hooks: "hooks",
|
|
9818
|
+
mcpConnection: "mcp_connection",
|
|
9819
|
+
migration: "migration",
|
|
9820
|
+
installSteps: "install_steps",
|
|
9821
|
+
uncaughtFailure: "uncaught_failure",
|
|
9822
|
+
toolsFailed: "tools_failed",
|
|
9823
|
+
doctor: "doctor"
|
|
9824
|
+
};
|
|
9825
|
+
});
|
|
9826
|
+
|
|
9827
|
+
// packages/workit-core/src/core/config.ts
|
|
9828
|
+
import {
|
|
9829
|
+
copyFileSync,
|
|
9830
|
+
cpSync,
|
|
9831
|
+
existsSync as existsSync3,
|
|
9832
|
+
mkdirSync as mkdirSync2,
|
|
9833
|
+
readFileSync as readFileSync2,
|
|
9834
|
+
readdirSync as readdirSync2,
|
|
9835
|
+
writeFileSync as writeFileSync2
|
|
9836
|
+
} from "node:fs";
|
|
9837
|
+
import os from "node:os";
|
|
9838
|
+
import path5 from "node:path";
|
|
9839
|
+
var diagnosticLogger, PRESETS, 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"), migratedDir = null, migrationFailed = false, ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
9840
|
+
if (migratedDir === dir)
|
|
9841
|
+
return dir;
|
|
9842
|
+
if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
|
|
9843
|
+
migratedDir = dir;
|
|
9844
|
+
return dir;
|
|
9845
|
+
}
|
|
9846
|
+
const legacy = path5.join(process.env.XDG_CONFIG_HOME || path5.join(os.homedir(), ".config"), "workflow-toolkit");
|
|
9847
|
+
if (!existsSync3(legacy)) {
|
|
9848
|
+
migratedDir = dir;
|
|
9849
|
+
return dir;
|
|
9850
|
+
}
|
|
9851
|
+
if (!migrationFailed && existsSync3(dir)) {
|
|
9852
|
+
migratedDir = dir;
|
|
9853
|
+
return dir;
|
|
9854
|
+
}
|
|
9855
|
+
migrationFailed = false;
|
|
9856
|
+
mkdirSync2(dir, { recursive: true });
|
|
9857
|
+
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
9858
|
+
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
9859
|
+
const src = path5.join(legacy, entry.name);
|
|
9860
|
+
const dest = path5.join(dir, entry.name);
|
|
9861
|
+
if (existsSync3(dest))
|
|
9862
|
+
continue;
|
|
9863
|
+
try {
|
|
9864
|
+
if (entry.isDirectory())
|
|
9865
|
+
cpSync(src, dest, { recursive: true });
|
|
9866
|
+
else if (entry.isFile())
|
|
9867
|
+
copyFileSync(src, dest);
|
|
9868
|
+
} catch (err) {
|
|
9869
|
+
migrationFailed = true;
|
|
9870
|
+
diagnosticLogger?.warn(EVENT.migration, { from: src, ok: false, ...errorDetail(err) });
|
|
9871
|
+
console.warn(`[workit] config migration: a file could not be copied to ${dir}; it will be retried on the next run`);
|
|
9872
|
+
}
|
|
9873
|
+
}
|
|
9874
|
+
if (!migrationFailed)
|
|
9875
|
+
migratedDir = dir;
|
|
9876
|
+
return dir;
|
|
9877
|
+
}, configDir = () => ensureConfigDir(), DEFAULTS, isConfigObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9878
|
+
var init_config = __esm(() => {
|
|
9879
|
+
init_boundary();
|
|
9880
|
+
PRESETS = {
|
|
9881
|
+
gitflow: {
|
|
9882
|
+
allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
|
|
9883
|
+
protected: ["main", "develop", "master", "prod", "production"]
|
|
9884
|
+
},
|
|
9885
|
+
"github-flow": { allowed: ["*"], protected: ["main"] },
|
|
9886
|
+
"trunk-based": { allowed: ["*"], protected: ["main"] },
|
|
9887
|
+
custom: { allowed: [], protected: [] }
|
|
9888
|
+
};
|
|
9889
|
+
DEFAULTS = {
|
|
9890
|
+
locale: "en",
|
|
9891
|
+
localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
|
|
9892
|
+
timezone: "America/Santiago",
|
|
9893
|
+
branchPolicy: {
|
|
9894
|
+
preset: "gitflow",
|
|
9895
|
+
allowed: [...PRESETS.gitflow.allowed],
|
|
9896
|
+
protected: [...PRESETS.gitflow.protected]
|
|
9897
|
+
},
|
|
9898
|
+
commitPolicy: { preset: "conventional" }
|
|
9899
|
+
};
|
|
9900
|
+
});
|
|
9901
|
+
|
|
9902
|
+
// packages/workit-core/src/core/workspaces.ts
|
|
9903
|
+
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
9904
|
+
import path6 from "node:path";
|
|
9905
|
+
var readWorkspacesResult = (dir = configDir()) => {
|
|
9906
|
+
const file = path6.join(dir, "workspaces.json");
|
|
9907
|
+
let raw;
|
|
9908
|
+
try {
|
|
9909
|
+
raw = readFileSync3(file, "utf8");
|
|
9910
|
+
} catch {
|
|
9911
|
+
return { status: "missing", path: file, entries: [] };
|
|
9912
|
+
}
|
|
9913
|
+
let parsed;
|
|
9914
|
+
try {
|
|
9915
|
+
parsed = JSON.parse(raw);
|
|
9916
|
+
} catch {
|
|
9917
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not valid JSON` };
|
|
9918
|
+
}
|
|
9919
|
+
if (!isConfigObject(parsed)) {
|
|
9920
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
9921
|
+
}
|
|
9922
|
+
const list = parsed.workspaces;
|
|
9923
|
+
return {
|
|
9924
|
+
status: "valid",
|
|
9925
|
+
path: file,
|
|
9926
|
+
entries: Array.isArray(list) ? list : []
|
|
9927
|
+
};
|
|
9928
|
+
}, loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries, globToRegExp = (glob) => {
|
|
9929
|
+
let out = "";
|
|
9930
|
+
for (let i = 0;i < glob.length; i++) {
|
|
9931
|
+
const c = glob[i];
|
|
9932
|
+
if (c === "*") {
|
|
9933
|
+
if (glob[i + 1] === "*") {
|
|
9934
|
+
if (glob[i + 2] === "/") {
|
|
9935
|
+
if (out === "")
|
|
9936
|
+
out += "/?";
|
|
9937
|
+
out += "(?:[^/]+/)*";
|
|
9938
|
+
i += 2;
|
|
9939
|
+
} else {
|
|
9940
|
+
if (i + 2 >= glob.length) {
|
|
9941
|
+
if (out.endsWith("/")) {
|
|
9942
|
+
out = out.slice(0, -1);
|
|
9943
|
+
out += "(?:/.*)?";
|
|
9944
|
+
} else {
|
|
9945
|
+
out += ".*";
|
|
9946
|
+
}
|
|
9947
|
+
} else {
|
|
9948
|
+
out += ".*";
|
|
9949
|
+
}
|
|
9950
|
+
i++;
|
|
9951
|
+
}
|
|
9952
|
+
} else {
|
|
9953
|
+
out += "[^/]*";
|
|
9954
|
+
}
|
|
9955
|
+
} else {
|
|
9956
|
+
out += c.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
9957
|
+
}
|
|
9958
|
+
}
|
|
9959
|
+
return new RegExp(`^${out}$`);
|
|
9960
|
+
}, matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/")), realpathOf = (p) => {
|
|
9961
|
+
const real = realpathSync2.native ?? realpathSync2;
|
|
9962
|
+
try {
|
|
9963
|
+
return real(p);
|
|
9964
|
+
} catch {
|
|
9965
|
+
return p;
|
|
9966
|
+
}
|
|
9967
|
+
}, canonicalGlob = (glob) => {
|
|
9968
|
+
const m = /[*?[\]{]/.exec(glob);
|
|
9969
|
+
const prefix = m ? glob.slice(0, m.index) : glob;
|
|
9970
|
+
const rest = m ? glob.slice(m.index) : "";
|
|
9971
|
+
if (!prefix)
|
|
9972
|
+
return glob;
|
|
9973
|
+
const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
|
|
9974
|
+
return real + (prefix.endsWith("/") ? "/" : "") + rest;
|
|
9975
|
+
}, resolveWorkspaceFrom = (cwd, dir) => {
|
|
9976
|
+
const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
|
|
9977
|
+
for (const entry of loadWorkspacesFrom(dir)) {
|
|
9978
|
+
if (!entry || typeof entry !== "object")
|
|
9979
|
+
continue;
|
|
9980
|
+
const ws = entry;
|
|
9981
|
+
if (typeof ws.glob !== "string" || !ws.glob)
|
|
9982
|
+
continue;
|
|
9983
|
+
const glob = ws.glob.replaceAll("\\", "/");
|
|
9984
|
+
const canonical = canonicalGlob(glob);
|
|
9985
|
+
for (const target of targets) {
|
|
9986
|
+
if (matchWorkspace(glob, target))
|
|
9987
|
+
return ws;
|
|
9988
|
+
if (canonical !== glob && matchWorkspace(canonical, target))
|
|
9989
|
+
return ws;
|
|
9990
|
+
}
|
|
9991
|
+
}
|
|
9992
|
+
return null;
|
|
9993
|
+
};
|
|
9994
|
+
var init_workspaces = __esm(() => {
|
|
9995
|
+
init_config();
|
|
9996
|
+
});
|
|
9997
|
+
|
|
9998
|
+
// packages/workit-core/src/core/git.ts
|
|
9999
|
+
var init_git = () => {};
|
|
10000
|
+
|
|
10001
|
+
// packages/workit-core/src/core/vcs-config.ts
|
|
10002
|
+
var init_vcs_config = __esm(() => {
|
|
10003
|
+
init_config();
|
|
10004
|
+
init_workspaces();
|
|
10005
|
+
init_branch();
|
|
10006
|
+
});
|
|
10007
|
+
|
|
10008
|
+
// packages/workit-core/src/core/branch.ts
|
|
10009
|
+
import { execFileSync } from "node:child_process";
|
|
10010
|
+
var verifyPushIdentity = (root, provider, account) => {
|
|
10011
|
+
if (!account || typeof account !== "string")
|
|
10012
|
+
return { ok: false, error: "push identity requires a configured area account" };
|
|
10013
|
+
const probe = provider === "gitlab" ? ["api", "user", "--jq", ".username"] : ["api", "user", "--jq", ".login"];
|
|
10014
|
+
const bin = provider === "gitlab" ? "glab" : "gh";
|
|
10015
|
+
let login;
|
|
10016
|
+
try {
|
|
10017
|
+
login = execFileSync(bin, [...probe], {
|
|
10018
|
+
cwd: root,
|
|
10019
|
+
encoding: "utf8",
|
|
10020
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10021
|
+
}).trim();
|
|
10022
|
+
} catch {
|
|
10023
|
+
return { ok: false, error: `${bin} identity could not be resolved` };
|
|
10024
|
+
}
|
|
10025
|
+
if (!login || login.toLowerCase() !== account.toLowerCase())
|
|
10026
|
+
return {
|
|
10027
|
+
ok: false,
|
|
10028
|
+
error: `push identity ${login || "(unknown)"} does not match area account ${account}`
|
|
10029
|
+
};
|
|
10030
|
+
return { ok: true };
|
|
10031
|
+
};
|
|
10032
|
+
var init_branch = __esm(() => {
|
|
10033
|
+
init_git();
|
|
10034
|
+
init_config();
|
|
10035
|
+
init_workspaces();
|
|
10036
|
+
init_vcs_config();
|
|
10037
|
+
});
|
|
10038
|
+
|
|
10039
|
+
// packages/workit-core/src/core/auto-approval.ts
|
|
9798
10040
|
import { createHash as createHash3 } from "node:crypto";
|
|
10041
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
10042
|
+
var AUTO_CLASSES, OPERATION_CLASS, operationAutoClass = (operation) => typeof operation === "string" ? OPERATION_CLASS[operation] ?? null : null, digestOf = (configDirPath) => {
|
|
10043
|
+
try {
|
|
10044
|
+
const raw = readFileSync4(`${configDirPath}/workspaces.json`, "utf8");
|
|
10045
|
+
return createHash3("sha256").update(raw).digest("hex");
|
|
10046
|
+
} catch {
|
|
10047
|
+
return null;
|
|
10048
|
+
}
|
|
10049
|
+
}, normalizeClasses = (value) => {
|
|
10050
|
+
if (value === true)
|
|
10051
|
+
return [...AUTO_CLASSES];
|
|
10052
|
+
if (!Array.isArray(value))
|
|
10053
|
+
return [];
|
|
10054
|
+
const seen = new Set;
|
|
10055
|
+
for (const item of value) {
|
|
10056
|
+
if (typeof item === "string" && AUTO_CLASSES.includes(item))
|
|
10057
|
+
seen.add(item);
|
|
10058
|
+
}
|
|
10059
|
+
return [...seen];
|
|
10060
|
+
}, resolveAutoApproval = (root, dir = configDir()) => {
|
|
10061
|
+
let match;
|
|
10062
|
+
try {
|
|
10063
|
+
match = resolveWorkspaceFrom(root, dir);
|
|
10064
|
+
} catch {
|
|
10065
|
+
return { status: "off" };
|
|
10066
|
+
}
|
|
10067
|
+
if (!match)
|
|
10068
|
+
return { status: "off" };
|
|
10069
|
+
const classes = normalizeClasses(match.autoApprove);
|
|
10070
|
+
if (classes.length === 0)
|
|
10071
|
+
return { status: "off" };
|
|
10072
|
+
return {
|
|
10073
|
+
status: "on",
|
|
10074
|
+
workspace: match.name,
|
|
10075
|
+
classes,
|
|
10076
|
+
account: typeof match.vcs?.account === "string" && match.vcs.account ? match.vcs.account : null,
|
|
10077
|
+
configDigest: digestOf(dir)
|
|
10078
|
+
};
|
|
10079
|
+
}, isStandingReceipt = (value) => typeof value === "object" && value !== null && value.kind === "standing", standingApprovalLive = (root, receipt, cls, dir) => {
|
|
10080
|
+
if (!isStandingReceipt(receipt))
|
|
10081
|
+
return false;
|
|
10082
|
+
if (typeof cls !== "string" || !AUTO_CLASSES.includes(cls))
|
|
10083
|
+
return false;
|
|
10084
|
+
if (receipt.class !== cls)
|
|
10085
|
+
return false;
|
|
10086
|
+
const resolved = resolveAutoApproval(root, dir);
|
|
10087
|
+
if (resolved.status !== "on")
|
|
10088
|
+
return false;
|
|
10089
|
+
if (resolved.workspace !== receipt.workspace)
|
|
10090
|
+
return false;
|
|
10091
|
+
return resolved.classes.includes(cls);
|
|
10092
|
+
}, standingReceiptFor = (root, workspace, cls, dir) => {
|
|
10093
|
+
const resolved = resolveAutoApproval(root, dir);
|
|
10094
|
+
return {
|
|
10095
|
+
kind: "standing",
|
|
10096
|
+
workspace,
|
|
10097
|
+
class: cls,
|
|
10098
|
+
configDigest: resolved.status === "on" && resolved.workspace === workspace ? resolved.configDigest : null
|
|
10099
|
+
};
|
|
10100
|
+
}, verifyStandingApproval = (root, task, caller, binding) => {
|
|
10101
|
+
if (task.status !== "active")
|
|
10102
|
+
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
10103
|
+
const session = task.intent?.provenance?.session;
|
|
10104
|
+
if (!session || session.kind !== "host" || session.host !== caller.host || session.handle !== caller.actor)
|
|
10105
|
+
return failure("permission_denied", "standing approval requires the lead session");
|
|
10106
|
+
const standing = binding.standing;
|
|
10107
|
+
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
10108
|
+
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
10109
|
+
let operation;
|
|
10110
|
+
try {
|
|
10111
|
+
operation = typeof binding.approvedContent === "string" ? JSON.parse(binding.approvedContent).operation : null;
|
|
10112
|
+
} catch {
|
|
10113
|
+
operation = null;
|
|
10114
|
+
}
|
|
10115
|
+
const cls = operationAutoClass(operation);
|
|
10116
|
+
if (!cls || cls !== standing.class)
|
|
10117
|
+
return failure("permission_denied", "standing approval does not cover this operation");
|
|
10118
|
+
const resolved = resolveAutoApproval(root);
|
|
10119
|
+
if (resolved.status !== "on" || resolved.workspace !== standing.workspace || !resolved.classes.includes(cls))
|
|
10120
|
+
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
10121
|
+
if (cls === "push") {
|
|
10122
|
+
const workspace = resolveWorkspaceFrom(root, configDir());
|
|
10123
|
+
const provider = workspace?.vcs?.provider;
|
|
10124
|
+
const account = resolved.account;
|
|
10125
|
+
if (!provider || !account)
|
|
10126
|
+
return failure("permission_denied", "auto-push requires a workspace provider and area account");
|
|
10127
|
+
const identity = verifyPushIdentity(root, provider, account);
|
|
10128
|
+
if (!identity.ok)
|
|
10129
|
+
return failure("permission_denied", identity.error);
|
|
10130
|
+
}
|
|
10131
|
+
return success(null, null, {
|
|
10132
|
+
kind: "host_observed",
|
|
10133
|
+
host: caller.host,
|
|
10134
|
+
session: { kind: "host", host: caller.host, handle: caller.actor },
|
|
10135
|
+
workerId: null,
|
|
10136
|
+
receipts: [standingReceiptFor(root, standing.workspace, cls)]
|
|
10137
|
+
});
|
|
10138
|
+
};
|
|
10139
|
+
var init_auto_approval = __esm(() => {
|
|
10140
|
+
init_workspaces();
|
|
10141
|
+
init_config();
|
|
10142
|
+
init_branch();
|
|
10143
|
+
init_task_contract();
|
|
10144
|
+
AUTO_CLASSES = ["branch", "commit", "push", "pr", "merge"];
|
|
10145
|
+
OPERATION_CLASS = {
|
|
10146
|
+
"git.branch_setup": "branch",
|
|
10147
|
+
"git.commit": "commit",
|
|
10148
|
+
"git.push": "push",
|
|
10149
|
+
"hosting.pull_request": "pr",
|
|
10150
|
+
"hosting.merge": "merge"
|
|
10151
|
+
};
|
|
10152
|
+
});
|
|
10153
|
+
|
|
10154
|
+
// packages/workit-core/src/core/authority.ts
|
|
10155
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
9799
10156
|
import * as fs5 from "node:fs";
|
|
9800
|
-
import
|
|
10157
|
+
import path7 from "node:path";
|
|
9801
10158
|
function applicableDecision(task, purpose, binding, checkoutRoot) {
|
|
9802
10159
|
if (binding.taskId !== task.id || binding.workspaceId !== task.workspaceId)
|
|
9803
10160
|
return [];
|
|
@@ -10199,7 +10556,7 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10199
10556
|
const record = verifiedAuthorities.get(authority);
|
|
10200
10557
|
verifiedAuthorities.delete(authority);
|
|
10201
10558
|
return record?.provenance ?? null;
|
|
10202
|
-
}, authorityMatches = (authority, expected) => authority.kind === "action" && authority.taskId === expected.taskId && authority.workspaceId === expected.workspaceId && authority.decisionId === expected.decisionId && sameRef(authority.actionRef, expected.actionRef) && authority.taskRevision === expected.taskRevision && authority.workspaceRevision === expected.workspaceRevision && authority.outcome === expected.outcome, authorityDecisionMatches = (authority, decision) => authority.purpose === decision.purpose && authority.response === decision.response && authority.digest === decision.digest && canonicalJson(authority.binding) === canonicalJson(decision.binding) && canonicalJson(authority.requirementIds) === canonicalJson(decision.requirementIds), provenanceMatches = (left, right) => left.kind === "host_observed" && right.kind === "host_observed" && left.host === right.host && canonicalJson(left.session) === canonicalJson(right.session), digestBytes2 = (bytes) =>
|
|
10559
|
+
}, authorityMatches = (authority, expected) => authority.kind === "action" && authority.taskId === expected.taskId && authority.workspaceId === expected.workspaceId && authority.decisionId === expected.decisionId && sameRef(authority.actionRef, expected.actionRef) && authority.taskRevision === expected.taskRevision && authority.workspaceRevision === expected.workspaceRevision && authority.outcome === expected.outcome, authorityDecisionMatches = (authority, decision) => authority.purpose === decision.purpose && authority.response === decision.response && authority.digest === decision.digest && canonicalJson(authority.binding) === canonicalJson(decision.binding) && canonicalJson(authority.requirementIds) === canonicalJson(decision.requirementIds), provenanceMatches = (left, right) => left.kind === "host_observed" && right.kind === "host_observed" && left.host === right.host && canonicalJson(left.session) === canonicalJson(right.session), digestBytes2 = (bytes) => createHash4("sha256").update(bytes).digest("hex"), verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
10203
10560
|
let root;
|
|
10204
10561
|
try {
|
|
10205
10562
|
root = fs5.realpathSync(checkoutRoot);
|
|
@@ -10211,23 +10568,23 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10211
10568
|
continue;
|
|
10212
10569
|
if (!reference.digest)
|
|
10213
10570
|
return failure("invalid_input", "document references require a byte digest");
|
|
10214
|
-
const target =
|
|
10215
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
10571
|
+
const target = path7.resolve(root, reference.path);
|
|
10572
|
+
if (target !== root && !target.startsWith(`${root}${path7.sep}`))
|
|
10216
10573
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
10217
10574
|
fields: [
|
|
10218
10575
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
10219
10576
|
]
|
|
10220
10577
|
});
|
|
10221
10578
|
try {
|
|
10222
|
-
const relative =
|
|
10579
|
+
const relative = path7.relative(root, target);
|
|
10223
10580
|
let current = root;
|
|
10224
|
-
for (const segment of relative.split(
|
|
10581
|
+
for (const segment of relative.split(path7.sep)) {
|
|
10225
10582
|
if (!segment)
|
|
10226
10583
|
continue;
|
|
10227
|
-
current =
|
|
10584
|
+
current = path7.join(current, segment);
|
|
10228
10585
|
const stat = fs5.lstatSync(current);
|
|
10229
10586
|
const real = fs5.realpathSync(current);
|
|
10230
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
10587
|
+
if (real !== root && !real.startsWith(`${root}${path7.sep}`))
|
|
10231
10588
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
10232
10589
|
fields: [
|
|
10233
10590
|
{
|
|
@@ -10277,6 +10634,20 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10277
10634
|
return failure("permission_denied", "decision was rejected");
|
|
10278
10635
|
if (entry.provenance.kind !== "host_observed" || entry.provenance.receipts.length === 0 || !provenanceMatches(entry.provenance, authority.provenance))
|
|
10279
10636
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
10637
|
+
const receipts = entry.provenance.receipts;
|
|
10638
|
+
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
10639
|
+
const questionBacked = receipts.some((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "host");
|
|
10640
|
+
if (!questionBacked && standing.length > 0) {
|
|
10641
|
+
let operation;
|
|
10642
|
+
try {
|
|
10643
|
+
operation = JSON.parse(decision.binding.approvedContent).operation;
|
|
10644
|
+
} catch {
|
|
10645
|
+
operation = null;
|
|
10646
|
+
}
|
|
10647
|
+
const cls = operationAutoClass(operation);
|
|
10648
|
+
if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls)))
|
|
10649
|
+
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
10650
|
+
}
|
|
10280
10651
|
if (decision.revoked)
|
|
10281
10652
|
return failure("permission_denied", "decision is revoked");
|
|
10282
10653
|
if (decision.digest !== decisionDigest(decision))
|
|
@@ -10300,6 +10671,7 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10300
10671
|
return success(null, null, { entry, workflow });
|
|
10301
10672
|
}, bindingCovers, verifyDecisionContent;
|
|
10302
10673
|
var init_authority = __esm(() => {
|
|
10674
|
+
init_auto_approval();
|
|
10303
10675
|
init_task_contract();
|
|
10304
10676
|
verifiedAuthorities = new WeakMap;
|
|
10305
10677
|
bindingCovers = scopeCovers;
|
|
@@ -10307,10 +10679,10 @@ var init_authority = __esm(() => {
|
|
|
10307
10679
|
});
|
|
10308
10680
|
|
|
10309
10681
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
10310
|
-
import { createHash as
|
|
10682
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
10311
10683
|
import * as fs6 from "node:fs";
|
|
10312
10684
|
import { spawnSync } from "node:child_process";
|
|
10313
|
-
import
|
|
10685
|
+
import path8 from "node:path";
|
|
10314
10686
|
function captureCandidate(root, scope, environment = []) {
|
|
10315
10687
|
let checkout;
|
|
10316
10688
|
try {
|
|
@@ -10352,7 +10724,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
10352
10724
|
for (const item of [...names].sort()) {
|
|
10353
10725
|
if (!scopeMatches(item, normalizedScope))
|
|
10354
10726
|
continue;
|
|
10355
|
-
const target =
|
|
10727
|
+
const target = path8.join(checkout, item);
|
|
10356
10728
|
try {
|
|
10357
10729
|
const stat = fs6.lstatSync(target);
|
|
10358
10730
|
if (stat.isSymbolicLink()) {
|
|
@@ -10623,12 +10995,12 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10623
10995
|
requirementIds: evaluations.map((item) => item.requirementId)
|
|
10624
10996
|
});
|
|
10625
10997
|
}
|
|
10626
|
-
var digestBytes3 = (value) =>
|
|
10998
|
+
var digestBytes3 = (value) => createHash5("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path8.sep}`), relative = (root, candidate) => path8.relative(root, candidate).split(path8.sep).join("/") || ".", canonicalPath = (value) => {
|
|
10627
10999
|
if (typeof value !== "string" || !value)
|
|
10628
11000
|
return null;
|
|
10629
11001
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
10630
11002
|
return null;
|
|
10631
|
-
const normalized =
|
|
11003
|
+
const normalized = path8.posix.normalize(value);
|
|
10632
11004
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
10633
11005
|
return null;
|
|
10634
11006
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -10670,14 +11042,14 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
|
|
|
10670
11042
|
git: /not a git repository/i.test(stderr) ? false : true
|
|
10671
11043
|
};
|
|
10672
11044
|
}
|
|
10673
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
11045
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path8.sep).join("/")) : [];
|
|
10674
11046
|
const stagedDeleted = spawnSync("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
10675
11047
|
if (stagedDeleted.status !== 0) {
|
|
10676
11048
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
10677
11049
|
if (!/not a git repository/i.test(stderr))
|
|
10678
11050
|
return { paths, uncertain: true, git: true };
|
|
10679
11051
|
} else if (stagedDeleted.stdout) {
|
|
10680
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
11052
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path8.sep).join("/")));
|
|
10681
11053
|
}
|
|
10682
11054
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
10683
11055
|
}, walk = (root, directory, output) => {
|
|
@@ -10691,7 +11063,7 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
|
|
|
10691
11063
|
for (const name of names) {
|
|
10692
11064
|
if (name === ".git" || name === ".workit")
|
|
10693
11065
|
continue;
|
|
10694
|
-
const target =
|
|
11066
|
+
const target = path8.join(directory, name);
|
|
10695
11067
|
let stat;
|
|
10696
11068
|
try {
|
|
10697
11069
|
stat = fs6.lstatSync(target);
|
|
@@ -10708,12 +11080,12 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
|
|
|
10708
11080
|
}, scopeRoots = (root, scope) => {
|
|
10709
11081
|
const roots = [];
|
|
10710
11082
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
10711
|
-
const target =
|
|
11083
|
+
const target = path8.resolve(root, item);
|
|
10712
11084
|
if (!inside(root, target))
|
|
10713
11085
|
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` }] });
|
|
10714
11086
|
let ancestor = target;
|
|
10715
11087
|
while (!fs6.existsSync(ancestor) && ancestor !== root)
|
|
10716
|
-
ancestor =
|
|
11088
|
+
ancestor = path8.dirname(ancestor);
|
|
10717
11089
|
try {
|
|
10718
11090
|
if (!inside(root, fs6.realpathSync(ancestor)))
|
|
10719
11091
|
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` }] });
|
|
@@ -10991,58 +11363,6 @@ var init_route_intent = __esm(() => {
|
|
|
10991
11363
|
init_task_store();
|
|
10992
11364
|
});
|
|
10993
11365
|
|
|
10994
|
-
// packages/workit-core/src/core/git.ts
|
|
10995
|
-
var init_git = () => {};
|
|
10996
|
-
|
|
10997
|
-
// packages/workit-core/src/core/boundary.ts
|
|
10998
|
-
var init_boundary = () => {};
|
|
10999
|
-
|
|
11000
|
-
// packages/workit-core/src/core/config.ts
|
|
11001
|
-
var PRESETS, DEFAULTS;
|
|
11002
|
-
var init_config = __esm(() => {
|
|
11003
|
-
init_boundary();
|
|
11004
|
-
PRESETS = {
|
|
11005
|
-
gitflow: {
|
|
11006
|
-
allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
|
|
11007
|
-
protected: ["main", "develop", "master", "prod", "production"]
|
|
11008
|
-
},
|
|
11009
|
-
"github-flow": { allowed: ["*"], protected: ["main"] },
|
|
11010
|
-
"trunk-based": { allowed: ["*"], protected: ["main"] },
|
|
11011
|
-
custom: { allowed: [], protected: [] }
|
|
11012
|
-
};
|
|
11013
|
-
DEFAULTS = {
|
|
11014
|
-
locale: "en",
|
|
11015
|
-
localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
|
|
11016
|
-
timezone: "America/Santiago",
|
|
11017
|
-
branchPolicy: {
|
|
11018
|
-
preset: "gitflow",
|
|
11019
|
-
allowed: [...PRESETS.gitflow.allowed],
|
|
11020
|
-
protected: [...PRESETS.gitflow.protected]
|
|
11021
|
-
},
|
|
11022
|
-
commitPolicy: { preset: "conventional" }
|
|
11023
|
-
};
|
|
11024
|
-
});
|
|
11025
|
-
|
|
11026
|
-
// packages/workit-core/src/core/workspaces.ts
|
|
11027
|
-
var init_workspaces = __esm(() => {
|
|
11028
|
-
init_config();
|
|
11029
|
-
});
|
|
11030
|
-
|
|
11031
|
-
// packages/workit-core/src/core/vcs-config.ts
|
|
11032
|
-
var init_vcs_config = __esm(() => {
|
|
11033
|
-
init_config();
|
|
11034
|
-
init_workspaces();
|
|
11035
|
-
init_branch();
|
|
11036
|
-
});
|
|
11037
|
-
|
|
11038
|
-
// packages/workit-core/src/core/branch.ts
|
|
11039
|
-
var init_branch = __esm(() => {
|
|
11040
|
-
init_git();
|
|
11041
|
-
init_config();
|
|
11042
|
-
init_workspaces();
|
|
11043
|
-
init_vcs_config();
|
|
11044
|
-
});
|
|
11045
|
-
|
|
11046
11366
|
// packages/workit-core/src/core/runtime-identity.ts
|
|
11047
11367
|
var init_runtime_identity = () => {};
|
|
11048
11368
|
|
|
@@ -11084,6 +11404,13 @@ var init_external_action = __esm(() => {
|
|
|
11084
11404
|
babysit: boolean2().optional()
|
|
11085
11405
|
}).strict()
|
|
11086
11406
|
}).strict(),
|
|
11407
|
+
object({
|
|
11408
|
+
operation: literal("hosting.merge"),
|
|
11409
|
+
payload: object({
|
|
11410
|
+
target_branch: string2().min(1).optional(),
|
|
11411
|
+
source_branch: string2().min(1).optional()
|
|
11412
|
+
}).strict()
|
|
11413
|
+
}).strict(),
|
|
11087
11414
|
object({
|
|
11088
11415
|
operation: literal("youtrack.update"),
|
|
11089
11416
|
payload: object({
|
|
@@ -11464,7 +11791,7 @@ var init_policy_resolver = __esm(() => {
|
|
|
11464
11791
|
});
|
|
11465
11792
|
|
|
11466
11793
|
// packages/workit-core/src/core/task-engine.ts
|
|
11467
|
-
import { realpathSync as
|
|
11794
|
+
import { realpathSync as realpathSync5 } from "node:fs";
|
|
11468
11795
|
|
|
11469
11796
|
class WorkitCore {
|
|
11470
11797
|
store;
|
|
@@ -11479,7 +11806,7 @@ class WorkitCore {
|
|
|
11479
11806
|
}
|
|
11480
11807
|
contextRootError() {
|
|
11481
11808
|
try {
|
|
11482
|
-
if (
|
|
11809
|
+
if (realpathSync5(this.context.root) !== this.store.root)
|
|
11483
11810
|
return failure("invalid_input", "operation context root does not match the task store root");
|
|
11484
11811
|
} catch {
|
|
11485
11812
|
return failure("invalid_input", "operation context root cannot be resolved");
|
|
@@ -11825,7 +12152,10 @@ class WorkitCore {
|
|
|
11825
12152
|
observeDecision(request, observation) {
|
|
11826
12153
|
return this.recordDecision(request, observation, true);
|
|
11827
12154
|
}
|
|
11828
|
-
|
|
12155
|
+
observeStandingDecision(request) {
|
|
12156
|
+
return this.recordDecision(request, undefined, true, true);
|
|
12157
|
+
}
|
|
12158
|
+
recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
|
|
11829
12159
|
const root = this.contextRootError();
|
|
11830
12160
|
if (!root.ok)
|
|
11831
12161
|
return root;
|
|
@@ -11840,8 +12170,10 @@ class WorkitCore {
|
|
|
11840
12170
|
return failure("permission_denied", "stated choices cannot authorize mutating actions");
|
|
11841
12171
|
if (stated && !input.binding?.statedChoice)
|
|
11842
12172
|
return failure("invalid_input", "stated choices require binding.statedChoice");
|
|
11843
|
-
if (nativeRequired && nativeObservation === undefined && !stated)
|
|
12173
|
+
if (nativeRequired && nativeObservation === undefined && !stated && !standing)
|
|
11844
12174
|
return failure("permission_denied", "native decision observation is required");
|
|
12175
|
+
if (standing && (!input.binding?.standing || input.purpose !== "action" || input.response !== "approved"))
|
|
12176
|
+
return failure("invalid_input", "standing approvals need an approved action binding with a standing rule");
|
|
11845
12177
|
const task = this.store.readTask(input.taskId);
|
|
11846
12178
|
if (!task.ok)
|
|
11847
12179
|
return task;
|
|
@@ -11873,7 +12205,10 @@ class WorkitCore {
|
|
|
11873
12205
|
consumption: null
|
|
11874
12206
|
};
|
|
11875
12207
|
const data = { ...base, digest: decisionDigest(base) };
|
|
11876
|
-
const
|
|
12208
|
+
const standingApproval = standing && !stated ? verifyStandingApproval(this.store.root, task.data, this.context.caller, input.binding) : null;
|
|
12209
|
+
if (standingApproval && !standingApproval.ok)
|
|
12210
|
+
return standingApproval;
|
|
12211
|
+
const native = nativeRequired && !stated && !standingApproval ? verifyNativeDecision(this.context.nativeAuthority, {
|
|
11877
12212
|
observation: nativeObservation,
|
|
11878
12213
|
expected: {
|
|
11879
12214
|
taskId: task.data.id,
|
|
@@ -11904,7 +12239,7 @@ class WorkitCore {
|
|
|
11904
12239
|
const entry = {
|
|
11905
12240
|
id: newId(),
|
|
11906
12241
|
recordedAt: mutation.now,
|
|
11907
|
-
provenance: nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
12242
|
+
provenance: standingApproval?.ok === true ? standingApproval.data : nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
11908
12243
|
data
|
|
11909
12244
|
};
|
|
11910
12245
|
return success(mutation.revision, null, {
|
|
@@ -12732,7 +13067,7 @@ class WorkitCore {
|
|
|
12732
13067
|
if (view.task.workspaceId !== view.workspace.id)
|
|
12733
13068
|
return failure("invalid_input", "task and workspace bindings are invalid");
|
|
12734
13069
|
try {
|
|
12735
|
-
if (
|
|
13070
|
+
if (realpathSync5(view.workspace.root) !== this.store.root)
|
|
12736
13071
|
return failure("invalid_input", "task view root does not match the task store");
|
|
12737
13072
|
} catch {
|
|
12738
13073
|
return failure("invalid_input", "task view root cannot be resolved");
|
|
@@ -13330,6 +13665,7 @@ var init_task_engine = __esm(() => {
|
|
|
13330
13665
|
init_task_contract();
|
|
13331
13666
|
init_authority();
|
|
13332
13667
|
init_policy_resolver();
|
|
13668
|
+
init_auto_approval();
|
|
13333
13669
|
init_task_context();
|
|
13334
13670
|
init_workers();
|
|
13335
13671
|
init_task_evaluation();
|
|
@@ -13345,6 +13681,7 @@ var init_core3 = __esm(() => {
|
|
|
13345
13681
|
init_methods();
|
|
13346
13682
|
init_route_intent();
|
|
13347
13683
|
init_branch();
|
|
13684
|
+
init_auto_approval();
|
|
13348
13685
|
init_runtime_identity();
|
|
13349
13686
|
init_authority();
|
|
13350
13687
|
init_authority();
|
|
@@ -13356,8 +13693,8 @@ var init_core3 = __esm(() => {
|
|
|
13356
13693
|
});
|
|
13357
13694
|
|
|
13358
13695
|
// packages/workit-cursor/hooks/workit-hook.ts
|
|
13359
|
-
import { existsSync as
|
|
13360
|
-
import
|
|
13696
|
+
import { existsSync as existsSync5, realpathSync as realpathSync6 } from "node:fs";
|
|
13697
|
+
import path9 from "node:path";
|
|
13361
13698
|
var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapabilities = (availability = {}) => {
|
|
13362
13699
|
const has = (name) => availability[name] === true;
|
|
13363
13700
|
return [
|
|
@@ -13418,7 +13755,7 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
|
|
|
13418
13755
|
if (!Array.isArray(roots) || roots.length !== 1 || !roots.every(nonEmpty2))
|
|
13419
13756
|
return { ok: false, error: "exactly one workspace root is required" };
|
|
13420
13757
|
const root = roots[0];
|
|
13421
|
-
if (!
|
|
13758
|
+
if (!path9.isAbsolute(root) || !existsSync5(root))
|
|
13422
13759
|
return { ok: false, error: "workspace root must be an existing absolute path" };
|
|
13423
13760
|
const event = value.hook_event_name;
|
|
13424
13761
|
const conversationId = nonEmpty2(value.conversation_id) ? value.conversation_id : undefined;
|
|
@@ -13553,7 +13890,7 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
|
|
|
13553
13890
|
return isRecord2(raw) && raw.hook_event_name === "sessionStart" ? {} : deny(parsed.error);
|
|
13554
13891
|
}
|
|
13555
13892
|
const input = parsed.data;
|
|
13556
|
-
const root =
|
|
13893
|
+
const root = realpathSync6(input.workspace_roots[0]);
|
|
13557
13894
|
if (input.hook_event_name === "sessionStart") {
|
|
13558
13895
|
const actor = input.session_id ?? input.conversation_id;
|
|
13559
13896
|
let compact = "";
|