@brainervirus/workit-mcp 1.2.0 → 1.3.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/dist/index.js +955 -315
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6620,11 +6620,11 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6620
6620
|
|
|
6621
6621
|
// packages/workit-mcp/src/index.ts
|
|
6622
6622
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6623
|
-
import
|
|
6623
|
+
import path16 from "node:path";
|
|
6624
6624
|
|
|
6625
6625
|
// packages/workit-mcp/src/server.ts
|
|
6626
|
-
import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as
|
|
6627
|
-
import
|
|
6626
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as realpathSync8 } from "node:fs";
|
|
6627
|
+
import path15 from "node:path";
|
|
6628
6628
|
|
|
6629
6629
|
// node_modules/@modelcontextprotocol/sdk/node_modules/zod/v4/core/core.js
|
|
6630
6630
|
var NEVER = Object.freeze({
|
|
@@ -12477,8 +12477,8 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
12477
12477
|
|
|
12478
12478
|
// packages/workit-core/src/core.ts
|
|
12479
12479
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
12480
|
-
import { existsSync as existsSync5, realpathSync as
|
|
12481
|
-
import
|
|
12480
|
+
import { existsSync as existsSync5, realpathSync as realpathSync7 } from "node:fs";
|
|
12481
|
+
import path11 from "node:path";
|
|
12482
12482
|
|
|
12483
12483
|
// packages/workit-core/src/core/task-contract.ts
|
|
12484
12484
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -17785,6 +17785,17 @@ function _array2(Class, element, params) {
|
|
|
17785
17785
|
...normalizeParams2(params)
|
|
17786
17786
|
});
|
|
17787
17787
|
}
|
|
17788
|
+
function _custom2(Class, fn, _params) {
|
|
17789
|
+
const norm = normalizeParams2(_params);
|
|
17790
|
+
norm.abort ?? (norm.abort = true);
|
|
17791
|
+
const schema = new Class({
|
|
17792
|
+
type: "custom",
|
|
17793
|
+
check: "custom",
|
|
17794
|
+
fn,
|
|
17795
|
+
...norm
|
|
17796
|
+
});
|
|
17797
|
+
return schema;
|
|
17798
|
+
}
|
|
17788
17799
|
function _refine2(Class, fn, _params) {
|
|
17789
17800
|
const schema = new Class({
|
|
17790
17801
|
type: "custom",
|
|
@@ -19938,6 +19949,9 @@ var ZodCustom2 = /* @__PURE__ */ $constructor2("ZodCustom", (inst, def) => {
|
|
|
19938
19949
|
ZodType2.init(inst, def);
|
|
19939
19950
|
inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx, json, params);
|
|
19940
19951
|
});
|
|
19952
|
+
function custom2(fn, _params) {
|
|
19953
|
+
return _custom2(ZodCustom2, fn ?? (() => true), _params);
|
|
19954
|
+
}
|
|
19941
19955
|
function refine2(fn, _params = {}) {
|
|
19942
19956
|
return _refine2(ZodCustom2, fn, _params);
|
|
19943
19957
|
}
|
|
@@ -20371,7 +20385,8 @@ var decisionSchema = object4({
|
|
|
20371
20385
|
consumption: object4({
|
|
20372
20386
|
state: _enum2(["reserved", "consumed", "uncertain"]),
|
|
20373
20387
|
at: utc,
|
|
20374
|
-
actionRef: refSchema
|
|
20388
|
+
actionRef: refSchema,
|
|
20389
|
+
historicalActionId: text.optional()
|
|
20375
20390
|
}).strict().nullable()
|
|
20376
20391
|
}).strict();
|
|
20377
20392
|
var findingSchema = object4({
|
|
@@ -20402,6 +20417,7 @@ var workerSchema = object4({
|
|
|
20402
20417
|
assignment: assignmentSchema,
|
|
20403
20418
|
state: _enum2(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
20404
20419
|
session: refSchema.nullable(),
|
|
20420
|
+
coordinator: refSchema.optional(),
|
|
20405
20421
|
report: workerReportSchema.nullable()
|
|
20406
20422
|
}).strict();
|
|
20407
20423
|
var ownerSchema = object4({
|
|
@@ -20513,6 +20529,7 @@ var taskSummarySchema = object4({
|
|
|
20513
20529
|
requirements: array3(requirementEvaluationSchema),
|
|
20514
20530
|
writer: workspaceRecordSchema.shape.writer
|
|
20515
20531
|
}).strict();
|
|
20532
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true }).extend({ source: provenanceSchema.pick({ host: true, kind: true }) }).strict();
|
|
20516
20533
|
var exportBundleSchema = object4({
|
|
20517
20534
|
schemaVersion: literal3(1),
|
|
20518
20535
|
exportedAt: utc,
|
|
@@ -20534,11 +20551,16 @@ var taskOperations = {
|
|
|
20534
20551
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
20535
20552
|
intent: intentSchema
|
|
20536
20553
|
}),
|
|
20537
|
-
list: operation({
|
|
20554
|
+
list: operation({
|
|
20555
|
+
action: literal3("list"),
|
|
20556
|
+
status: _enum2(["open", "closed", "all"]).optional(),
|
|
20557
|
+
query: string5().trim().min(1).max(200).optional(),
|
|
20558
|
+
limit: number5().int().min(1).max(50).optional()
|
|
20559
|
+
}),
|
|
20538
20560
|
inspect: operation({
|
|
20539
20561
|
action: literal3("inspect"),
|
|
20540
20562
|
...taskId,
|
|
20541
|
-
view: _enum2(["summary", "full"])
|
|
20563
|
+
view: _enum2(["summary", "full"]).optional()
|
|
20542
20564
|
}),
|
|
20543
20565
|
revise: operation({
|
|
20544
20566
|
action: literal3("revise"),
|
|
@@ -20566,7 +20588,7 @@ var taskOperations = {
|
|
|
20566
20588
|
...taskId,
|
|
20567
20589
|
...revisions,
|
|
20568
20590
|
expectedWorkspaceRevision: revision.optional(),
|
|
20569
|
-
authorityRefs: array3(refSchema)
|
|
20591
|
+
authorityRefs: array3(refSchema).optional()
|
|
20570
20592
|
}),
|
|
20571
20593
|
close: operation({
|
|
20572
20594
|
action: literal3("close"),
|
|
@@ -20880,6 +20902,7 @@ function requirementId(input) {
|
|
|
20880
20902
|
}
|
|
20881
20903
|
// packages/workit-core/src/core/task-store.ts
|
|
20882
20904
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
20905
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
20883
20906
|
import * as fs4 from "node:fs";
|
|
20884
20907
|
import { hostname as hostname2 } from "node:os";
|
|
20885
20908
|
import path4 from "node:path";
|
|
@@ -21515,7 +21538,8 @@ var metadataLockSchema = object4({
|
|
|
21515
21538
|
pid: number5().int().nonnegative().safe(),
|
|
21516
21539
|
processStart: string5().nullable(),
|
|
21517
21540
|
host: string5().min(1),
|
|
21518
|
-
nonce: string5().min(1)
|
|
21541
|
+
nonce: string5().min(1),
|
|
21542
|
+
externalAction: literal3(true).optional()
|
|
21519
21543
|
}).strict();
|
|
21520
21544
|
var now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
21521
21545
|
var cachedRuntimeVersion = null;
|
|
@@ -21567,6 +21591,28 @@ var sameMetadataLock = (left, right) => {
|
|
|
21567
21591
|
const parsed = metadataLockSchema.safeParse(left);
|
|
21568
21592
|
return parsed.success && canonicalJson(parsed.data) === canonicalJson(right);
|
|
21569
21593
|
};
|
|
21594
|
+
var sameDirectoryIdentity = (left, right) => {
|
|
21595
|
+
if (!path4.isAbsolute(left) || !path4.isAbsolute(right))
|
|
21596
|
+
return false;
|
|
21597
|
+
const normalizedLeft = path4.resolve(left);
|
|
21598
|
+
const normalizedRight = path4.resolve(right);
|
|
21599
|
+
try {
|
|
21600
|
+
const a = fs4.statSync(normalizedLeft, { bigint: true });
|
|
21601
|
+
const b = fs4.statSync(normalizedRight, { bigint: true });
|
|
21602
|
+
if (!a.isDirectory() || !b.isDirectory())
|
|
21603
|
+
return false;
|
|
21604
|
+
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
21605
|
+
return true;
|
|
21606
|
+
const canonicalLeft = fs4.realpathSync(normalizedLeft);
|
|
21607
|
+
const canonicalRight = fs4.realpathSync(normalizedRight);
|
|
21608
|
+
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
21609
|
+
return true;
|
|
21610
|
+
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
21611
|
+
} catch {
|
|
21612
|
+
return false;
|
|
21613
|
+
}
|
|
21614
|
+
};
|
|
21615
|
+
var externalActionLockRoots = new AsyncLocalStorage;
|
|
21570
21616
|
|
|
21571
21617
|
class TaskStore {
|
|
21572
21618
|
root;
|
|
@@ -21627,7 +21673,7 @@ class TaskStore {
|
|
|
21627
21673
|
const item = this.readRecord(this.workspacePath, workspaceRecordSchema);
|
|
21628
21674
|
if (!item.exists)
|
|
21629
21675
|
return success(null, null, null);
|
|
21630
|
-
if (item.result.ok && item.result.data.root !== this.root)
|
|
21676
|
+
if (item.result.ok && item.result.data.root !== this.root && !sameDirectoryIdentity(item.result.data.root, this.root))
|
|
21631
21677
|
return failure("recovery_required", "workspace root binding is invalid", {
|
|
21632
21678
|
path: this.workspacePath
|
|
21633
21679
|
});
|
|
@@ -21718,6 +21764,29 @@ class TaskStore {
|
|
|
21718
21764
|
const current = this.readWorkspace();
|
|
21719
21765
|
if (!current.ok)
|
|
21720
21766
|
return current;
|
|
21767
|
+
const origin = input.task.origin;
|
|
21768
|
+
if (current.data && origin) {
|
|
21769
|
+
if (origin.workspaceId === current.data.id) {
|
|
21770
|
+
const source = this.readTask(origin.taskId);
|
|
21771
|
+
if (source.ok)
|
|
21772
|
+
return success(source.data.revision, current.data.revision, source.data);
|
|
21773
|
+
if (source.code !== "not_found")
|
|
21774
|
+
return source;
|
|
21775
|
+
return failure("not_found", "source task is not present in this shared workspace");
|
|
21776
|
+
}
|
|
21777
|
+
const tasks = this.listTasks();
|
|
21778
|
+
if (!tasks.ok)
|
|
21779
|
+
return tasks;
|
|
21780
|
+
const prior = tasks.data.filter((task) => task.origin?.workspaceId === origin.workspaceId && task.origin?.taskId === origin.taskId);
|
|
21781
|
+
if (prior.length > 1)
|
|
21782
|
+
return failure("recovery_required", `source task has multiple imported mappings: ${prior.map((task) => task.id).join(", ")}`);
|
|
21783
|
+
if (prior.length === 1) {
|
|
21784
|
+
const existing = prior[0];
|
|
21785
|
+
if (existing.origin.exportDigest !== origin.exportDigest)
|
|
21786
|
+
return failure("revision_conflict", "source task export changed; reconcile the mapped destination task before importing", { taskId: existing.id });
|
|
21787
|
+
return success(existing.revision, current.data.revision, existing);
|
|
21788
|
+
}
|
|
21789
|
+
}
|
|
21721
21790
|
if (current.data ? input.expectedWorkspaceRevision !== current.data.revision : input.expectedWorkspaceRevision !== null)
|
|
21722
21791
|
return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
|
|
21723
21792
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
@@ -22106,6 +22175,73 @@ class TaskStore {
|
|
|
22106
22175
|
})
|
|
22107
22176
|
};
|
|
22108
22177
|
}
|
|
22178
|
+
externalActionLockOptions() {
|
|
22179
|
+
const options = this.metadataLockOptions();
|
|
22180
|
+
return { ...options, payload: () => ({ ...options.payload(), externalAction: true }) };
|
|
22181
|
+
}
|
|
22182
|
+
async withExternalActionLock(operation, reentrant = false) {
|
|
22183
|
+
const activeRoots = externalActionLockRoots.getStore();
|
|
22184
|
+
if (activeRoots && [...activeRoots].some((root) => root === this.root || sameDirectoryIdentity(root, this.root)))
|
|
22185
|
+
return operation();
|
|
22186
|
+
try {
|
|
22187
|
+
this.initializeMutationStorage();
|
|
22188
|
+
} catch (error) {
|
|
22189
|
+
return failure("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
22190
|
+
path: this.workitDir
|
|
22191
|
+
});
|
|
22192
|
+
}
|
|
22193
|
+
let handle;
|
|
22194
|
+
try {
|
|
22195
|
+
handle = acquireFileLockSync(this.workspacePath, this.externalActionLockOptions());
|
|
22196
|
+
} catch (error) {
|
|
22197
|
+
return this.lockFailure(error);
|
|
22198
|
+
}
|
|
22199
|
+
let result;
|
|
22200
|
+
try {
|
|
22201
|
+
if (!handle.verifyStillHeld())
|
|
22202
|
+
result = failure("recovery_required", "metadata lock was compromised", {
|
|
22203
|
+
path: this.lockPath
|
|
22204
|
+
});
|
|
22205
|
+
else {
|
|
22206
|
+
try {
|
|
22207
|
+
const run = () => operation();
|
|
22208
|
+
result = await (reentrant ? externalActionLockRoots.run(new Set([...activeRoots ?? [], this.root]), run) : run());
|
|
22209
|
+
} catch {
|
|
22210
|
+
result = failure("external_outcome_unknown", "external action outcome is unknown", {
|
|
22211
|
+
outcome: "unknown"
|
|
22212
|
+
});
|
|
22213
|
+
}
|
|
22214
|
+
try {
|
|
22215
|
+
if (!handle.verifyStillHeld())
|
|
22216
|
+
result = failure("external_outcome_unknown", "metadata lock was compromised", {
|
|
22217
|
+
outcome: "unknown",
|
|
22218
|
+
path: this.lockPath
|
|
22219
|
+
});
|
|
22220
|
+
} catch {
|
|
22221
|
+
result = failure("external_outcome_unknown", "metadata lock could not be verified", {
|
|
22222
|
+
outcome: "unknown",
|
|
22223
|
+
path: this.lockPath
|
|
22224
|
+
});
|
|
22225
|
+
}
|
|
22226
|
+
}
|
|
22227
|
+
} catch {
|
|
22228
|
+
result = failure("external_outcome_unknown", "external action lock operation failed", {
|
|
22229
|
+
outcome: "unknown",
|
|
22230
|
+
path: this.lockPath
|
|
22231
|
+
});
|
|
22232
|
+
}
|
|
22233
|
+
try {
|
|
22234
|
+
handle.release();
|
|
22235
|
+
} catch (error) {
|
|
22236
|
+
const released = this.lockFailure(error);
|
|
22237
|
+
const releaseError = released.ok ? "metadata lock release failed" : released.error;
|
|
22238
|
+
return result.ok ? failure("external_outcome_unknown", "external action metadata lock release is uncertain", {
|
|
22239
|
+
outcome: "unknown",
|
|
22240
|
+
path: this.lockPath
|
|
22241
|
+
}) : failure("recovery_required", `${result.error}; ${releaseError}`, result.details);
|
|
22242
|
+
}
|
|
22243
|
+
return result;
|
|
22244
|
+
}
|
|
22109
22245
|
recoveryLockOptions(observed, evidence, gate) {
|
|
22110
22246
|
const options = this.metadataLockOptions();
|
|
22111
22247
|
options.staleRecovery = "remove-if-unchanged";
|
|
@@ -22175,6 +22311,14 @@ class TaskStore {
|
|
|
22175
22311
|
lockFailure(error) {
|
|
22176
22312
|
const value = error;
|
|
22177
22313
|
const code = typeof value?.code === "string" ? value.code : "";
|
|
22314
|
+
if (code === "EEXIST" || code === "file_lock_timeout") {
|
|
22315
|
+
const lock = this.readLockSnapshot();
|
|
22316
|
+
if (lock.ok && lock.data?.data.externalAction)
|
|
22317
|
+
return failure("writer_conflict", "workspace is reserved by a managed external action", {
|
|
22318
|
+
outcome: "not_started",
|
|
22319
|
+
path: this.lockPath
|
|
22320
|
+
});
|
|
22321
|
+
}
|
|
22178
22322
|
const recovery = code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || code === "metadata_lock_invalid" || code === "not-file" || /metadata lock|file lock|reclaim/i.test(String(value?.message ?? error));
|
|
22179
22323
|
return failure(recovery ? "recovery_required" : "storage_error", `metadata lock operation failed: ${String(error)}`, { path: this.lockPath });
|
|
22180
22324
|
}
|
|
@@ -22399,6 +22543,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
|
|
|
22399
22543
|
}) && !value.split(/[\\/]/).includes("..");
|
|
22400
22544
|
var callerValue = (value) => ({ host: value.host, actor: value.actor });
|
|
22401
22545
|
var workerIdOf = (value) => value.workerId ?? null;
|
|
22546
|
+
var currentWriterOwnsTask = (task, workspace, caller) => {
|
|
22547
|
+
const owner = workspace.writer?.owner;
|
|
22548
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
22549
|
+
};
|
|
22402
22550
|
function assertProductWriteAllowed(input) {
|
|
22403
22551
|
if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
|
|
22404
22552
|
return failure("invalid_input", "invalid product write path");
|
|
@@ -22438,21 +22586,23 @@ function assertProductWriteAllowed(input) {
|
|
|
22438
22586
|
}
|
|
22439
22587
|
|
|
22440
22588
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
22441
|
-
import { createHash as
|
|
22589
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
22442
22590
|
import * as fs7 from "node:fs";
|
|
22443
22591
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
22444
|
-
import
|
|
22592
|
+
import path10 from "node:path";
|
|
22445
22593
|
|
|
22446
22594
|
// packages/workit-core/src/core/authority.ts
|
|
22447
|
-
import { createHash as
|
|
22595
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
22448
22596
|
import * as fs6 from "node:fs";
|
|
22449
|
-
import
|
|
22597
|
+
import path9 from "node:path";
|
|
22450
22598
|
|
|
22451
22599
|
// packages/workit-core/src/core/auto-approval.ts
|
|
22452
|
-
import { createHash as
|
|
22453
|
-
import { readFileSync as readFileSync4 } from "node:fs";
|
|
22600
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
22601
|
+
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
22602
|
+
import path8 from "node:path";
|
|
22454
22603
|
|
|
22455
22604
|
// packages/workit-core/src/core/workspaces.ts
|
|
22605
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
22456
22606
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
22457
22607
|
import path6 from "node:path";
|
|
22458
22608
|
|
|
@@ -22470,7 +22620,7 @@ import os from "node:os";
|
|
|
22470
22620
|
import path5 from "node:path";
|
|
22471
22621
|
|
|
22472
22622
|
// packages/workit-core/src/core/boundary.ts
|
|
22473
|
-
import { statSync } from "node:fs";
|
|
22623
|
+
import { statSync as statSync2 } from "node:fs";
|
|
22474
22624
|
var EVENT = {
|
|
22475
22625
|
initialization: "initialization",
|
|
22476
22626
|
provenance: "provenance",
|
|
@@ -22498,7 +22648,7 @@ var changedSourcesSinceLoad = (marker) => {
|
|
|
22498
22648
|
const changed = [];
|
|
22499
22649
|
for (const file of marker.files) {
|
|
22500
22650
|
try {
|
|
22501
|
-
if (
|
|
22651
|
+
if (statSync2(file).mtimeMs > marker.loadedAt)
|
|
22502
22652
|
changed.push(file);
|
|
22503
22653
|
} catch {}
|
|
22504
22654
|
}
|
|
@@ -22657,33 +22807,197 @@ var COMMIT_PRESETS = [
|
|
|
22657
22807
|
"custom",
|
|
22658
22808
|
"auto"
|
|
22659
22809
|
];
|
|
22810
|
+
var resolveCommitPolicy = (config, workspace) => {
|
|
22811
|
+
const wp = workspace?.commitPolicy ?? {};
|
|
22812
|
+
if (COMMIT_PRESETS.includes(String(wp.preset ?? ""))) {
|
|
22813
|
+
return {
|
|
22814
|
+
preset: wp.preset,
|
|
22815
|
+
...typeof wp.pattern === "string" ? { pattern: wp.pattern } : {}
|
|
22816
|
+
};
|
|
22817
|
+
}
|
|
22818
|
+
return config.commitPolicy ?? { preset: "conventional" };
|
|
22819
|
+
};
|
|
22660
22820
|
|
|
22661
22821
|
// packages/workit-core/src/core/workspaces.ts
|
|
22822
|
+
var nonBlank = string5().refine((value) => value.trim().length > 0, "must not be blank");
|
|
22823
|
+
var branchPreset = custom2((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
|
|
22824
|
+
var commitPreset = custom2((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
|
|
22825
|
+
var workspaceBranchPolicySchema = object4({
|
|
22826
|
+
preset: branchPreset,
|
|
22827
|
+
developBranch: nonBlank.optional(),
|
|
22828
|
+
prefixes: object4({
|
|
22829
|
+
feature: nonBlank,
|
|
22830
|
+
bugfix: nonBlank,
|
|
22831
|
+
release: nonBlank,
|
|
22832
|
+
hotfix: nonBlank
|
|
22833
|
+
}).passthrough().optional(),
|
|
22834
|
+
allowed: array3(nonBlank).optional(),
|
|
22835
|
+
protected: array3(nonBlank).optional(),
|
|
22836
|
+
integration: _enum2(["pr", "merge"]).optional()
|
|
22837
|
+
}).strict();
|
|
22838
|
+
var workspaceCommitPolicySchema = object4({ preset: commitPreset, pattern: string5().optional() }).strict();
|
|
22839
|
+
var workspaceProfileSchema = object4({
|
|
22840
|
+
branchPolicy: workspaceBranchPolicySchema.optional(),
|
|
22841
|
+
commitPolicy: workspaceCommitPolicySchema.optional()
|
|
22842
|
+
}).strict().refine((profile) => profile.branchPolicy !== undefined || profile.commitPolicy !== undefined, {
|
|
22843
|
+
message: "a profile must define branchPolicy or commitPolicy"
|
|
22844
|
+
});
|
|
22845
|
+
var versionSourceSchema = discriminatedUnion2("kind", [
|
|
22846
|
+
object4({
|
|
22847
|
+
kind: literal3("package-json"),
|
|
22848
|
+
path: nonBlank,
|
|
22849
|
+
field: nonBlank
|
|
22850
|
+
}).strict(),
|
|
22851
|
+
object4({ kind: literal3("git-tag") }).strict(),
|
|
22852
|
+
object4({ kind: literal3("manual") }).strict()
|
|
22853
|
+
]);
|
|
22854
|
+
var releaseTrackSchema = object4({
|
|
22855
|
+
strategy: branchPreset,
|
|
22856
|
+
productionBranch: nonBlank,
|
|
22857
|
+
integrationBranch: nonBlank,
|
|
22858
|
+
naming: object4({ feature: nonBlank, release: nonBlank, hotfix: nonBlank }).strict(),
|
|
22859
|
+
baseBranch: nonBlank,
|
|
22860
|
+
mergeBackBranches: array3(nonBlank),
|
|
22861
|
+
pullRequestTarget: nonBlank,
|
|
22862
|
+
tagNamespace: string5(),
|
|
22863
|
+
versionSource: versionSourceSchema,
|
|
22864
|
+
requiredChecks: array3(nonBlank)
|
|
22865
|
+
}).strict().superRefine((track, context) => {
|
|
22866
|
+
for (const [field, values] of [
|
|
22867
|
+
["mergeBackBranches", track.mergeBackBranches],
|
|
22868
|
+
["requiredChecks", track.requiredChecks]
|
|
22869
|
+
]) {
|
|
22870
|
+
if (new Set(values).size !== values.length) {
|
|
22871
|
+
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
22872
|
+
}
|
|
22873
|
+
}
|
|
22874
|
+
if (track.versionSource.kind === "package-json" && (path6.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
22875
|
+
context.addIssue({
|
|
22876
|
+
code: "custom",
|
|
22877
|
+
path: ["versionSource", "path"],
|
|
22878
|
+
message: "must stay inside the repository"
|
|
22879
|
+
});
|
|
22880
|
+
}
|
|
22881
|
+
});
|
|
22882
|
+
var workspaceConfigSchema = object4({
|
|
22883
|
+
name: nonBlank,
|
|
22884
|
+
glob: nonBlank,
|
|
22885
|
+
vcs: object4({
|
|
22886
|
+
provider: _enum2(["gitlab", "github"]),
|
|
22887
|
+
defaultTargetBranch: nonBlank.optional(),
|
|
22888
|
+
tokenFile: nonBlank.optional(),
|
|
22889
|
+
account: nonBlank.optional()
|
|
22890
|
+
}).passthrough().optional(),
|
|
22891
|
+
autoApprove: union3([boolean5(), array3(nonBlank)]).optional(),
|
|
22892
|
+
youtrack: object4({ baseUrl: nonBlank.optional(), link_issues: boolean5().optional() }).passthrough().optional(),
|
|
22893
|
+
issues: object4({ provider: literal3("github").optional(), link_on_pr: boolean5().optional() }).passthrough().optional(),
|
|
22894
|
+
branchPolicy: workspaceBranchPolicySchema.optional(),
|
|
22895
|
+
commitPolicy: workspaceCommitPolicySchema.optional(),
|
|
22896
|
+
defaultProfile: nonBlank.optional(),
|
|
22897
|
+
profiles: record3(nonBlank, workspaceProfileSchema).optional(),
|
|
22898
|
+
releaseTracks: record3(nonBlank, releaseTrackSchema).optional()
|
|
22899
|
+
}).passthrough().superRefine((workspace, context) => {
|
|
22900
|
+
if (workspace.youtrack && workspace.vcs?.provider !== "gitlab") {
|
|
22901
|
+
context.addIssue({
|
|
22902
|
+
code: "custom",
|
|
22903
|
+
path: ["youtrack"],
|
|
22904
|
+
message: `YouTrack issue linking requires the gitlab provider, got ${workspace.vcs?.provider ?? "unset"}`
|
|
22905
|
+
});
|
|
22906
|
+
}
|
|
22907
|
+
if (workspace.issues && workspace.vcs?.provider !== "github") {
|
|
22908
|
+
context.addIssue({
|
|
22909
|
+
code: "custom",
|
|
22910
|
+
path: ["issues"],
|
|
22911
|
+
message: `GitHub issue linking requires the github provider, got ${workspace.vcs?.provider ?? "unset"}`
|
|
22912
|
+
});
|
|
22913
|
+
}
|
|
22914
|
+
if (workspace.defaultProfile !== undefined && !Object.hasOwn(workspace.profiles ?? {}, workspace.defaultProfile)) {
|
|
22915
|
+
context.addIssue({
|
|
22916
|
+
code: "custom",
|
|
22917
|
+
path: ["defaultProfile"],
|
|
22918
|
+
message: `does not name a configured profile`
|
|
22919
|
+
});
|
|
22920
|
+
}
|
|
22921
|
+
});
|
|
22922
|
+
var workspacesFileSchema = object4({ workspaces: array3(workspaceConfigSchema).optional() }).passthrough().superRefine((file, context) => {
|
|
22923
|
+
const names = new Set;
|
|
22924
|
+
for (const [index, workspace] of (file.workspaces ?? []).entries()) {
|
|
22925
|
+
if (names.has(workspace.name)) {
|
|
22926
|
+
context.addIssue({
|
|
22927
|
+
code: "custom",
|
|
22928
|
+
path: ["workspaces", index, "name"],
|
|
22929
|
+
message: `duplicate workspace name ${JSON.stringify(workspace.name)}`
|
|
22930
|
+
});
|
|
22931
|
+
}
|
|
22932
|
+
names.add(workspace.name);
|
|
22933
|
+
}
|
|
22934
|
+
});
|
|
22935
|
+
var workspacesRevision = (contents) => createHash3("sha256").update(contents ?? "<missing>").digest("hex");
|
|
22936
|
+
var validateWorkspacesDocument = (parsed, file) => {
|
|
22937
|
+
if (!isConfigObject(parsed)) {
|
|
22938
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
22939
|
+
}
|
|
22940
|
+
const validated = workspacesFileSchema.safeParse(parsed, { reportInput: true });
|
|
22941
|
+
if (!validated.success) {
|
|
22942
|
+
const detail = validated.error.issues.map((issue) => {
|
|
22943
|
+
const field = String(issue.path[issue.path.length - 1] ?? "");
|
|
22944
|
+
const attempted = ["provider", "preset", "strategy"].includes(field) && typeof issue.input === "string" ? ` (${JSON.stringify(issue.input)})` : "";
|
|
22945
|
+
return `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}${attempted}`;
|
|
22946
|
+
}).join("; ");
|
|
22947
|
+
return {
|
|
22948
|
+
status: "invalid",
|
|
22949
|
+
path: file,
|
|
22950
|
+
entries: [],
|
|
22951
|
+
error: `${file} has invalid workspace configuration: ${detail}`
|
|
22952
|
+
};
|
|
22953
|
+
}
|
|
22954
|
+
for (const [index, workspace] of (validated.data.workspaces ?? []).entries()) {
|
|
22955
|
+
const glob = validateWorkspaceGlob(workspace.glob);
|
|
22956
|
+
if (!glob.ok) {
|
|
22957
|
+
return {
|
|
22958
|
+
status: "invalid",
|
|
22959
|
+
path: file,
|
|
22960
|
+
entries: [],
|
|
22961
|
+
error: `${file} workspaces.${index}.glob: ${glob.error}`
|
|
22962
|
+
};
|
|
22963
|
+
}
|
|
22964
|
+
}
|
|
22965
|
+
return {
|
|
22966
|
+
status: "valid",
|
|
22967
|
+
path: file,
|
|
22968
|
+
entries: validated.data.workspaces ?? [],
|
|
22969
|
+
document: parsed
|
|
22970
|
+
};
|
|
22971
|
+
};
|
|
22662
22972
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
22663
22973
|
const file = path6.join(dir, "workspaces.json");
|
|
22664
22974
|
let raw;
|
|
22665
22975
|
try {
|
|
22666
22976
|
raw = readFileSync3(file, "utf8");
|
|
22667
|
-
} catch {
|
|
22668
|
-
|
|
22977
|
+
} catch (error) {
|
|
22978
|
+
if (error.code === "ENOENT")
|
|
22979
|
+
return { status: "missing", path: file, entries: [], revision: workspacesRevision(null) };
|
|
22980
|
+
return {
|
|
22981
|
+
status: "malformed",
|
|
22982
|
+
path: file,
|
|
22983
|
+
entries: [],
|
|
22984
|
+
error: `${file} could not be read: ${error instanceof Error ? error.message : String(error)}`
|
|
22985
|
+
};
|
|
22669
22986
|
}
|
|
22670
22987
|
let parsed;
|
|
22671
22988
|
try {
|
|
22672
22989
|
parsed = JSON.parse(raw);
|
|
22673
22990
|
} catch {
|
|
22674
|
-
return {
|
|
22675
|
-
|
|
22676
|
-
|
|
22677
|
-
|
|
22991
|
+
return {
|
|
22992
|
+
status: "malformed",
|
|
22993
|
+
path: file,
|
|
22994
|
+
entries: [],
|
|
22995
|
+
revision: workspacesRevision(raw),
|
|
22996
|
+
error: `${file} is not valid JSON`
|
|
22997
|
+
};
|
|
22678
22998
|
}
|
|
22679
|
-
|
|
22680
|
-
return {
|
|
22681
|
-
status: "valid",
|
|
22682
|
-
path: file,
|
|
22683
|
-
entries: Array.isArray(list) ? list : []
|
|
22684
|
-
};
|
|
22999
|
+
return { ...validateWorkspacesDocument(parsed, file), revision: workspacesRevision(raw) };
|
|
22685
23000
|
};
|
|
22686
|
-
var loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries;
|
|
22687
23001
|
var globToRegExp = (glob) => {
|
|
22688
23002
|
let out = "";
|
|
22689
23003
|
for (let i = 0;i < glob.length; i++) {
|
|
@@ -22718,6 +23032,30 @@ var globToRegExp = (glob) => {
|
|
|
22718
23032
|
return new RegExp(`^${out}$`);
|
|
22719
23033
|
};
|
|
22720
23034
|
var matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/"));
|
|
23035
|
+
var UNSUPPORTED_GLOB = /[?[\]{}]/;
|
|
23036
|
+
var UNSUPPORTED_EXTGLOB = /[@+*]\(|!/;
|
|
23037
|
+
var validateWorkspaceGlob = (glob) => {
|
|
23038
|
+
const trimmed = glob.trim();
|
|
23039
|
+
if (!trimmed)
|
|
23040
|
+
return { ok: false, error: "workspace pattern is required" };
|
|
23041
|
+
const m = UNSUPPORTED_GLOB.exec(trimmed);
|
|
23042
|
+
if (m) {
|
|
23043
|
+
return {
|
|
23044
|
+
ok: false,
|
|
23045
|
+
error: `unsupported glob character ${JSON.stringify(m[0])} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
|
|
23046
|
+
};
|
|
23047
|
+
}
|
|
23048
|
+
const ext = UNSUPPORTED_EXTGLOB.exec(trimmed);
|
|
23049
|
+
if (ext) {
|
|
23050
|
+
const token = ext[0];
|
|
23051
|
+
const kind = token === "!" ? "negation (!)" : `extglob (${token})`;
|
|
23052
|
+
return {
|
|
23053
|
+
ok: false,
|
|
23054
|
+
error: `unsupported glob ${kind} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
|
|
23055
|
+
};
|
|
23056
|
+
}
|
|
23057
|
+
return { ok: true };
|
|
23058
|
+
};
|
|
22721
23059
|
var realpathOf = (p) => {
|
|
22722
23060
|
const real = realpathSync2.native ?? realpathSync2;
|
|
22723
23061
|
try {
|
|
@@ -22735,29 +23073,137 @@ var canonicalGlob = (glob) => {
|
|
|
22735
23073
|
const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
|
|
22736
23074
|
return real + (prefix.endsWith("/") ? "/" : "") + rest;
|
|
22737
23075
|
};
|
|
22738
|
-
var resolveWorkspaceFrom = (cwd, dir) => {
|
|
23076
|
+
var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
|
|
23077
|
+
const result = readWorkspacesResult(dir);
|
|
23078
|
+
if (result.status === "malformed" || result.status === "invalid")
|
|
23079
|
+
throw new Error(result.error);
|
|
22739
23080
|
const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
|
|
22740
|
-
|
|
22741
|
-
|
|
22742
|
-
continue;
|
|
22743
|
-
const ws = entry;
|
|
22744
|
-
if (typeof ws.glob !== "string" || !ws.glob)
|
|
22745
|
-
continue;
|
|
23081
|
+
const matches = [];
|
|
23082
|
+
for (const ws of result.entries) {
|
|
22746
23083
|
const glob = ws.glob.replaceAll("\\", "/");
|
|
22747
23084
|
const canonical = canonicalGlob(glob);
|
|
23085
|
+
let matched = false;
|
|
22748
23086
|
for (const target of targets) {
|
|
22749
|
-
if (matchWorkspace(glob, target))
|
|
22750
|
-
|
|
22751
|
-
|
|
22752
|
-
|
|
23087
|
+
if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
|
|
23088
|
+
matched = true;
|
|
23089
|
+
break;
|
|
23090
|
+
}
|
|
22753
23091
|
}
|
|
23092
|
+
if (matched)
|
|
23093
|
+
matches.push(ws);
|
|
22754
23094
|
}
|
|
22755
|
-
|
|
23095
|
+
if (workspaceName !== undefined) {
|
|
23096
|
+
const selected = matches.find((ws) => ws.name === workspaceName);
|
|
23097
|
+
if (!selected && matches.length > 0) {
|
|
23098
|
+
const matchingNames = matches.map((ws) => ws.name);
|
|
23099
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matchingNames.join(", ")}`);
|
|
23100
|
+
}
|
|
23101
|
+
if (selected)
|
|
23102
|
+
return selected;
|
|
23103
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
23104
|
+
}
|
|
23105
|
+
if (matches.length > 1) {
|
|
23106
|
+
throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${matches.map((ws) => ws.name).join(", ")}`);
|
|
23107
|
+
}
|
|
23108
|
+
return matches[0] ?? null;
|
|
23109
|
+
};
|
|
23110
|
+
var runtimeWorkspacePolicySchema = (kind) => {
|
|
23111
|
+
const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
|
|
23112
|
+
const policySchema = kind === "branch" ? workspaceBranchPolicySchema : kind === "commit" ? workspaceCommitPolicySchema : object4({
|
|
23113
|
+
provider: _enum2(["gitlab", "github"]),
|
|
23114
|
+
defaultTargetBranch: nonBlank.optional(),
|
|
23115
|
+
account: nonBlank.optional()
|
|
23116
|
+
}).passthrough();
|
|
23117
|
+
const profileSchema = object4({ [key]: policySchema.optional() }).passthrough();
|
|
23118
|
+
const entrySchema = object4({
|
|
23119
|
+
name: nonBlank,
|
|
23120
|
+
glob: nonBlank,
|
|
23121
|
+
[key]: policySchema.optional(),
|
|
23122
|
+
defaultProfile: nonBlank.optional(),
|
|
23123
|
+
profiles: record3(nonBlank, profileSchema).optional()
|
|
23124
|
+
}).passthrough();
|
|
23125
|
+
return entrySchema;
|
|
23126
|
+
};
|
|
23127
|
+
var resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd, "vcs");
|
|
23128
|
+
var runtimeWorkspaceIndexSchema = object4({
|
|
23129
|
+
workspaces: array3(object4({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
23130
|
+
}).passthrough();
|
|
23131
|
+
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
23132
|
+
const file = path6.join(configDir(), "workspaces.json");
|
|
23133
|
+
let raw;
|
|
23134
|
+
try {
|
|
23135
|
+
raw = readFileSync3(file, "utf8");
|
|
23136
|
+
} catch (error) {
|
|
23137
|
+
if (error.code === "ENOENT")
|
|
23138
|
+
return null;
|
|
23139
|
+
throw new Error(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
23140
|
+
}
|
|
23141
|
+
let parsed;
|
|
23142
|
+
try {
|
|
23143
|
+
parsed = JSON.parse(raw);
|
|
23144
|
+
} catch {
|
|
23145
|
+
throw new Error(`${file} is not valid JSON`);
|
|
23146
|
+
}
|
|
23147
|
+
const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
|
|
23148
|
+
if (!validated.success) {
|
|
23149
|
+
const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
|
|
23150
|
+
throw new Error(`${file} has invalid workspace matching data: ${detail}`);
|
|
23151
|
+
}
|
|
23152
|
+
const entries = validated.data.workspaces;
|
|
23153
|
+
for (const entry of entries ?? []) {
|
|
23154
|
+
const glob = validateWorkspaceGlob(entry.glob);
|
|
23155
|
+
if (!glob.ok)
|
|
23156
|
+
throw new Error(`${file} workspace ${entry.name}: ${glob.error}`);
|
|
23157
|
+
}
|
|
23158
|
+
const targets = [cwd, realpathOf(cwd)].map((value) => value.replaceAll("\\", "/"));
|
|
23159
|
+
const matches = (entries ?? []).filter((entry) => {
|
|
23160
|
+
const glob = entry.glob.replaceAll("\\", "/");
|
|
23161
|
+
const canonical = canonicalGlob(glob);
|
|
23162
|
+
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
23163
|
+
});
|
|
23164
|
+
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
23165
|
+
let selected;
|
|
23166
|
+
if (workspaceName) {
|
|
23167
|
+
const named = matches.filter((entry) => entry.name === workspaceName);
|
|
23168
|
+
if (named.length === 1)
|
|
23169
|
+
selected = named[0];
|
|
23170
|
+
else if (named.length > 1)
|
|
23171
|
+
throw new Error(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
|
|
23172
|
+
else if (matches.length)
|
|
23173
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
23174
|
+
else
|
|
23175
|
+
throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
23176
|
+
} else if (matches.length > 1)
|
|
23177
|
+
throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
23178
|
+
else
|
|
23179
|
+
selected = matches[0];
|
|
23180
|
+
if (!selected)
|
|
23181
|
+
return null;
|
|
23182
|
+
const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
|
|
23183
|
+
if (!policy.success) {
|
|
23184
|
+
const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
|
|
23185
|
+
throw new Error(`${file} has invalid ${kind} policy configuration: ${detail}`);
|
|
23186
|
+
}
|
|
23187
|
+
return policy.data;
|
|
23188
|
+
};
|
|
23189
|
+
var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
23190
|
+
const candidate = resolveRuntimeWorkspaceCandidate(cwd, kind);
|
|
23191
|
+
if (!candidate)
|
|
23192
|
+
return { policy: undefined, source: "user-default" };
|
|
23193
|
+
const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
|
|
23194
|
+
const profileName = requestedProfile?.trim() || candidate.defaultProfile;
|
|
23195
|
+
if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
|
|
23196
|
+
throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
|
|
23197
|
+
const profile = profileName ? candidate.profiles?.[profileName] : undefined;
|
|
23198
|
+
if (profileName && !profile)
|
|
23199
|
+
throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
|
|
23200
|
+
const profilePolicy = profile?.[key];
|
|
23201
|
+
const workspacePolicy = candidate[key];
|
|
23202
|
+
return {
|
|
23203
|
+
policy: profilePolicy ?? workspacePolicy,
|
|
23204
|
+
source: profilePolicy ? `profile:${profileName}` : workspacePolicy ? `workspace:${candidate.name}` : "user-default"
|
|
23205
|
+
};
|
|
22756
23206
|
};
|
|
22757
|
-
var resolveWorkspace = (cwd) => resolveWorkspaceFrom(cwd, configDir());
|
|
22758
|
-
|
|
22759
|
-
// packages/workit-core/src/core/branch.ts
|
|
22760
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
22761
23207
|
|
|
22762
23208
|
// packages/workit-core/src/core/git.ts
|
|
22763
23209
|
import { execFileSync } from "node:child_process";
|
|
@@ -22831,7 +23277,6 @@ var gitContext = (workspaceRoot, paths = []) => {
|
|
|
22831
23277
|
import fs5 from "node:fs";
|
|
22832
23278
|
import path7 from "node:path";
|
|
22833
23279
|
import { spawnSync } from "node:child_process";
|
|
22834
|
-
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
22835
23280
|
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path7.join(configDir(), "vcs.json");
|
|
22836
23281
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
22837
23282
|
var remoteProvider = (cwd) => {
|
|
@@ -22845,6 +23290,79 @@ var remoteProvider = (cwd) => {
|
|
|
22845
23290
|
return "gitlab";
|
|
22846
23291
|
return null;
|
|
22847
23292
|
};
|
|
23293
|
+
var remoteEndpoint = (remote) => {
|
|
23294
|
+
const raw = remote.trim();
|
|
23295
|
+
if (!raw)
|
|
23296
|
+
return null;
|
|
23297
|
+
const scp = /^(?:[^@\s]+@)?([^:]+):(.+)$/u.exec(raw);
|
|
23298
|
+
if (scp && !raw.includes("://"))
|
|
23299
|
+
return {
|
|
23300
|
+
host: scp[1].toLowerCase(),
|
|
23301
|
+
path: scp[2].replace(/^\//u, "").replace(/\.git$/u, ""),
|
|
23302
|
+
protocol: "ssh:",
|
|
23303
|
+
port: ""
|
|
23304
|
+
};
|
|
23305
|
+
try {
|
|
23306
|
+
const url = new URL(raw);
|
|
23307
|
+
return {
|
|
23308
|
+
host: url.hostname.toLowerCase(),
|
|
23309
|
+
path: url.pathname.replace(/^\//u, "").replace(/\.git$/u, "").replace(/\/$/u, ""),
|
|
23310
|
+
protocol: url.protocol,
|
|
23311
|
+
port: url.port
|
|
23312
|
+
};
|
|
23313
|
+
} catch {
|
|
23314
|
+
return null;
|
|
23315
|
+
}
|
|
23316
|
+
};
|
|
23317
|
+
var configuredSshEndpoint = (host) => {
|
|
23318
|
+
if (host.startsWith("-"))
|
|
23319
|
+
return null;
|
|
23320
|
+
const resolved = spawnSync("ssh", ["-G", host], {
|
|
23321
|
+
encoding: "utf8",
|
|
23322
|
+
timeout: 5000,
|
|
23323
|
+
maxBuffer: 1024 * 1024,
|
|
23324
|
+
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
23325
|
+
});
|
|
23326
|
+
if (resolved.status !== 0)
|
|
23327
|
+
return null;
|
|
23328
|
+
const output = resolved.stdout ?? "";
|
|
23329
|
+
const hostname = /^hostname\s+(\S+)/imu.exec(output)?.[1]?.toLowerCase();
|
|
23330
|
+
if (!hostname)
|
|
23331
|
+
return null;
|
|
23332
|
+
return { host: hostname, port: /^port\s+(\d+)/imu.exec(output)?.[1] ?? "22" };
|
|
23333
|
+
};
|
|
23334
|
+
var sshApiHostAliases = {
|
|
23335
|
+
"github.com": [{ host: "ssh.github.com", port: "443" }],
|
|
23336
|
+
"gitlab.com": [{ host: "altssh.gitlab.com", port: "443" }]
|
|
23337
|
+
};
|
|
23338
|
+
var hostingApiHostMatches = (remote, apiHost, resolveSshEndpoint = configuredSshEndpoint) => {
|
|
23339
|
+
const endpoint = remoteEndpoint(remote);
|
|
23340
|
+
if (!endpoint)
|
|
23341
|
+
return false;
|
|
23342
|
+
let expected;
|
|
23343
|
+
try {
|
|
23344
|
+
const url = new URL(`https://${apiHost}`);
|
|
23345
|
+
if (url.pathname !== "/" || url.search || url.hash || url.username || url.password)
|
|
23346
|
+
return false;
|
|
23347
|
+
expected = {
|
|
23348
|
+
host: url.hostname.toLowerCase().replace(/\.$/u, ""),
|
|
23349
|
+
port: url.port
|
|
23350
|
+
};
|
|
23351
|
+
} catch {
|
|
23352
|
+
return false;
|
|
23353
|
+
}
|
|
23354
|
+
if (endpoint.protocol !== "ssh:") {
|
|
23355
|
+
return endpoint.host.replace(/\.$/u, "") === expected.host && endpoint.port === expected.port;
|
|
23356
|
+
}
|
|
23357
|
+
const resolved = resolveSshEndpoint(endpoint.host);
|
|
23358
|
+
if (!resolved)
|
|
23359
|
+
return false;
|
|
23360
|
+
const hostname = resolved.host.toLowerCase().replace(/\.$/u, "");
|
|
23361
|
+
const port = endpoint.port || resolved.port;
|
|
23362
|
+
if (hostname === expected.host)
|
|
23363
|
+
return port === (expected.port || "22");
|
|
23364
|
+
return Boolean(sshApiHostAliases[expected.host]?.some((alias) => alias.host === hostname && alias.port === port && (!expected.port || expected.port === port)));
|
|
23365
|
+
};
|
|
22848
23366
|
var readVcsConfig = () => {
|
|
22849
23367
|
const file = vcsConfigPath();
|
|
22850
23368
|
let raw;
|
|
@@ -22865,7 +23383,7 @@ var readVcsConfig = () => {
|
|
|
22865
23383
|
return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
|
|
22866
23384
|
};
|
|
22867
23385
|
function vcsConfig(mode, cwd) {
|
|
22868
|
-
const ws =
|
|
23386
|
+
const ws = resolveRuntimeWorkspaceVcs(vcsCwd(cwd));
|
|
22869
23387
|
const wsVcs = ws?.vcs ?? {};
|
|
22870
23388
|
const wsYt = ws?.youtrack ?? {};
|
|
22871
23389
|
const wsIssues = ws?.issues ?? {};
|
|
@@ -22878,8 +23396,8 @@ function vcsConfig(mode, cwd) {
|
|
|
22878
23396
|
const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
|
|
22879
23397
|
const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
|
|
22880
23398
|
const defaultTarget = String(wsVcs.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : cfg.defaultTargetBranch ?? policyDefault) ?? "develop");
|
|
22881
|
-
const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
|
|
22882
|
-
const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
|
|
23399
|
+
const linkIssues = provider === "gitlab" && typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
|
|
23400
|
+
const youtrackBaseUrl = provider === "gitlab" && typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
|
|
22883
23401
|
let issuesProvider = null;
|
|
22884
23402
|
let linkOnPr = null;
|
|
22885
23403
|
if (provider === "github" && typeof wsIssues.provider === "string" && wsIssues.provider.toLowerCase() === "github") {
|
|
@@ -22903,9 +23421,8 @@ function vcsConfig(mode, cwd) {
|
|
|
22903
23421
|
if (cfgStatus === "malformed") {
|
|
22904
23422
|
return { ok: false, error: cfgError, configPath: cfgPath };
|
|
22905
23423
|
}
|
|
22906
|
-
if (cfgStatus === "missing")
|
|
23424
|
+
if (cfgStatus === "missing" && provider === null)
|
|
22907
23425
|
return { ok: false, error: `vcs.json is missing: ${cfgPath}`, configPath: cfgPath };
|
|
22908
|
-
}
|
|
22909
23426
|
if (provider === null) {
|
|
22910
23427
|
return {
|
|
22911
23428
|
ok: false,
|
|
@@ -22914,24 +23431,12 @@ function vcsConfig(mode, cwd) {
|
|
|
22914
23431
|
};
|
|
22915
23432
|
}
|
|
22916
23433
|
const prov = cfg[provider] ?? {};
|
|
22917
|
-
const wsTokenFile = typeof wsVcs.tokenFile === "string" && wsVcs.tokenFile.trim() !== "" ? wsVcs.tokenFile : null;
|
|
22918
|
-
const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path7.join(configDir(), `${provider}.token`));
|
|
22919
|
-
const tokenPath = path7.resolve(tokenFile);
|
|
22920
|
-
let tokenOk = false;
|
|
22921
|
-
if (fs5.existsSync(tokenPath)) {
|
|
22922
|
-
const token = fs5.readFileSync(tokenPath, "utf8").trim();
|
|
22923
|
-
const placeholder = !token || token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER);
|
|
22924
|
-
tokenOk = !placeholder;
|
|
22925
|
-
}
|
|
22926
23434
|
const out = {
|
|
22927
23435
|
ok: true,
|
|
22928
23436
|
configPath: path7.resolve(cfgPath),
|
|
22929
23437
|
provider,
|
|
22930
23438
|
defaultTargetBranch: defaultTarget,
|
|
22931
23439
|
pr: cfg.pr ?? {},
|
|
22932
|
-
tokenPath,
|
|
22933
|
-
tokenPresent: fs5.existsSync(tokenPath),
|
|
22934
|
-
tokenReady: tokenOk,
|
|
22935
23440
|
workspace_name: ws?.name ?? null,
|
|
22936
23441
|
link_issues: linkIssues,
|
|
22937
23442
|
youtrack_base_url: youtrackBaseUrl,
|
|
@@ -22946,16 +23451,58 @@ function vcsConfig(mode, cwd) {
|
|
|
22946
23451
|
} else if (provider === "github") {
|
|
22947
23452
|
out.github = { host: prov.host ?? "github.com" };
|
|
22948
23453
|
}
|
|
22949
|
-
if (mode === "summary")
|
|
22950
|
-
delete out.tokenPath;
|
|
22951
23454
|
return out;
|
|
22952
23455
|
}
|
|
23456
|
+
function vcsCliIdentity(cwd) {
|
|
23457
|
+
const root = vcsCwd(cwd);
|
|
23458
|
+
const cfg = vcsConfig("load", root);
|
|
23459
|
+
if (!cfg.ok)
|
|
23460
|
+
return { ok: false, error: cfg.error ?? "vcs config not ready" };
|
|
23461
|
+
const provider = cfg.provider;
|
|
23462
|
+
if (provider !== "gitlab" && provider !== "github")
|
|
23463
|
+
return { ok: false, error: `unsupported provider: ${provider}` };
|
|
23464
|
+
const bin = provider === "gitlab" ? "glab" : "gh";
|
|
23465
|
+
const host = String(provider === "github" ? cfg.github?.host ?? "github.com" : cfg.gitlab?.host ?? "gitlab.com").toLowerCase();
|
|
23466
|
+
const env = {
|
|
23467
|
+
...process.env,
|
|
23468
|
+
PATH: process.env.PATH ?? "",
|
|
23469
|
+
...provider === "github" ? { GH_HOST: host } : { GITLAB_HOST: host }
|
|
23470
|
+
};
|
|
23471
|
+
const result = spawnSync(bin, ["api", "user"], {
|
|
23472
|
+
cwd: root,
|
|
23473
|
+
encoding: "utf8",
|
|
23474
|
+
env
|
|
23475
|
+
});
|
|
23476
|
+
if (result.status !== 0)
|
|
23477
|
+
return { ok: false, provider, error: `${bin} is missing or not authenticated` };
|
|
23478
|
+
try {
|
|
23479
|
+
const user = JSON.parse(result.stdout ?? "");
|
|
23480
|
+
const username = provider === "gitlab" ? user.username : user.login;
|
|
23481
|
+
if (typeof username !== "string" || !username)
|
|
23482
|
+
return { ok: false, provider, error: `invalid ${bin} identity response` };
|
|
23483
|
+
const expected = resolveRuntimeWorkspaceVcs(root)?.vcs?.account;
|
|
23484
|
+
if (expected && username.toLowerCase() !== expected.toLowerCase())
|
|
23485
|
+
return {
|
|
23486
|
+
ok: false,
|
|
23487
|
+
provider,
|
|
23488
|
+
error: `${bin} account ${username} does not match ${expected}`
|
|
23489
|
+
};
|
|
23490
|
+
return { ok: true, provider, host, username, name: user.name };
|
|
23491
|
+
} catch {
|
|
23492
|
+
return { ok: false, provider, error: `invalid JSON from ${bin} api user` };
|
|
23493
|
+
}
|
|
23494
|
+
}
|
|
22953
23495
|
function mergedPrStyle(limit = 6, cwd) {
|
|
22954
23496
|
const cfg = vcsConfig("load", cwd);
|
|
22955
|
-
if (!cfg.ok
|
|
23497
|
+
if (!cfg.ok)
|
|
22956
23498
|
return { ok: false, error: "vcs not configured" };
|
|
22957
23499
|
const provider = cfg.provider;
|
|
22958
|
-
const
|
|
23500
|
+
const host = String(provider === "github" ? cfg.github?.host ?? "github.com" : cfg.gitlab?.host ?? "gitlab.com").toLowerCase();
|
|
23501
|
+
const env = {
|
|
23502
|
+
...process.env,
|
|
23503
|
+
PATH: process.env.PATH ?? "",
|
|
23504
|
+
...provider === "github" ? { GH_HOST: host } : { GITLAB_HOST: host }
|
|
23505
|
+
};
|
|
22959
23506
|
const examples = [];
|
|
22960
23507
|
const descInfo = (desc, caseInsensitiveNotes = false) => ({
|
|
22961
23508
|
hasNotesSection: caseInsensitiveNotes ? /##\s*notes/i.test(desc) : /##\s*Notes/.test(desc),
|
|
@@ -22963,23 +23510,30 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
22963
23510
|
`).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
|
|
22964
23511
|
descriptionPreview: desc.slice(0, 600)
|
|
22965
23512
|
});
|
|
23513
|
+
const remoteResult = spawnSync("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
23514
|
+
cwd,
|
|
23515
|
+
encoding: "utf8"
|
|
23516
|
+
});
|
|
23517
|
+
const remotes = (remoteResult.stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
|
|
23518
|
+
if (remotes.length === 0)
|
|
23519
|
+
return { ok: false, error: "no origin remote" };
|
|
23520
|
+
if (remotes.length !== 1)
|
|
23521
|
+
return { ok: false, error: "origin has ambiguous push destinations" };
|
|
23522
|
+
const remote = remotes[0];
|
|
23523
|
+
const endpoint = remoteEndpoint(remote);
|
|
23524
|
+
if (!endpoint?.path)
|
|
23525
|
+
return { ok: false, error: "hosting repository could not be resolved" };
|
|
23526
|
+
if (!hostingApiHostMatches(remote, host))
|
|
23527
|
+
return { ok: false, error: "PR style target does not match the provider host" };
|
|
22966
23528
|
if (provider === "gitlab") {
|
|
22967
|
-
const
|
|
22968
|
-
|
|
22969
|
-
|
|
22970
|
-
|
|
22971
|
-
|
|
22972
|
-
if (!m)
|
|
22973
|
-
return { ok: false, error: "not a gitlab.com origin" };
|
|
22974
|
-
const project = m[1];
|
|
22975
|
-
const env = { ...process.env, GITLAB_TOKEN: token };
|
|
22976
|
-
const run = (args) => spawnSync("glab", ["api", ...args], { cwd, encoding: "utf8", env });
|
|
23529
|
+
const run = (args) => spawnSync("glab", ["api", ...args], {
|
|
23530
|
+
cwd,
|
|
23531
|
+
encoding: "utf8",
|
|
23532
|
+
env
|
|
23533
|
+
});
|
|
22977
23534
|
let r = run([
|
|
22978
|
-
`projects/${
|
|
23535
|
+
`projects/${endpoint.path.replaceAll("/", "%2F")}/merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`
|
|
22979
23536
|
]);
|
|
22980
|
-
if (r.status !== 0) {
|
|
22981
|
-
r = run([`merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`]);
|
|
22982
|
-
}
|
|
22983
23537
|
if (r.status !== 0)
|
|
22984
23538
|
return { ok: false, error: "could not list merge requests" };
|
|
22985
23539
|
for (const mr of JSON.parse(r.stdout ?? "[]")) {
|
|
@@ -22992,7 +23546,18 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
22992
23546
|
});
|
|
22993
23547
|
}
|
|
22994
23548
|
} else if (provider === "github") {
|
|
22995
|
-
const r = spawnSync("gh", [
|
|
23549
|
+
const r = spawnSync("gh", [
|
|
23550
|
+
"pr",
|
|
23551
|
+
"list",
|
|
23552
|
+
"--repo",
|
|
23553
|
+
`${host}/${endpoint.path}`,
|
|
23554
|
+
"--state",
|
|
23555
|
+
"merged",
|
|
23556
|
+
"--limit",
|
|
23557
|
+
String(limit),
|
|
23558
|
+
"--json",
|
|
23559
|
+
"title,url,body"
|
|
23560
|
+
], { cwd, encoding: "utf8", env });
|
|
22996
23561
|
if (r.status !== 0)
|
|
22997
23562
|
return { ok: false, error: "could not list pull requests" };
|
|
22998
23563
|
for (const pr of JSON.parse(r.stdout ?? "[]")) {
|
|
@@ -23014,26 +23579,28 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
23014
23579
|
}
|
|
23015
23580
|
|
|
23016
23581
|
// packages/workit-core/src/core/branch.ts
|
|
23017
|
-
|
|
23582
|
+
function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
23583
|
+
const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
23584
|
+
const config = readConfig();
|
|
23585
|
+
return kind === "branch" ? {
|
|
23586
|
+
branchPolicy: resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null),
|
|
23587
|
+
provenance: { branchPolicy: selected.source }
|
|
23588
|
+
} : {
|
|
23589
|
+
commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
|
|
23590
|
+
provenance: { commitPolicy: selected.source }
|
|
23591
|
+
};
|
|
23592
|
+
}
|
|
23593
|
+
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
23018
23594
|
var verifyPushIdentity = (root, provider, account) => {
|
|
23019
23595
|
if (!account || typeof account !== "string")
|
|
23020
23596
|
return { ok: false, error: "push identity requires a configured area account" };
|
|
23021
|
-
const
|
|
23022
|
-
|
|
23023
|
-
|
|
23024
|
-
|
|
23025
|
-
login = execFileSync2(bin, [...probe], {
|
|
23026
|
-
cwd: root,
|
|
23027
|
-
encoding: "utf8",
|
|
23028
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
23029
|
-
}).trim();
|
|
23030
|
-
} catch {
|
|
23031
|
-
return { ok: false, error: `${bin} identity could not be resolved` };
|
|
23032
|
-
}
|
|
23033
|
-
if (!login || login.toLowerCase() !== account.toLowerCase())
|
|
23597
|
+
const identity = vcsCliIdentity(root);
|
|
23598
|
+
if (!identity.ok || identity.provider !== provider)
|
|
23599
|
+
return { ok: false, error: identity.error ?? "hosting CLI identity could not be resolved" };
|
|
23600
|
+
if (identity.username?.toLowerCase() !== account.toLowerCase())
|
|
23034
23601
|
return {
|
|
23035
23602
|
ok: false,
|
|
23036
|
-
error: `push identity ${
|
|
23603
|
+
error: `push identity ${identity.username || "(unknown)"} does not match area account ${account}`
|
|
23037
23604
|
};
|
|
23038
23605
|
return { ok: true };
|
|
23039
23606
|
};
|
|
@@ -23051,7 +23618,7 @@ var operationAutoClass = (operation) => typeof operation === "string" ? OPERATIO
|
|
|
23051
23618
|
var digestOf = (configDirPath) => {
|
|
23052
23619
|
try {
|
|
23053
23620
|
const raw = readFileSync4(`${configDirPath}/workspaces.json`, "utf8");
|
|
23054
|
-
return
|
|
23621
|
+
return createHash4("sha256").update(raw).digest("hex");
|
|
23055
23622
|
} catch {
|
|
23056
23623
|
return null;
|
|
23057
23624
|
}
|
|
@@ -23089,14 +23656,30 @@ var resolveAutoApproval = (root, dir = configDir()) => {
|
|
|
23089
23656
|
};
|
|
23090
23657
|
};
|
|
23091
23658
|
var isStandingReceipt = (value) => typeof value === "object" && value !== null && value.kind === "standing";
|
|
23092
|
-
var
|
|
23659
|
+
var targetOf = (root, descriptor) => {
|
|
23660
|
+
try {
|
|
23661
|
+
const parsed = typeof descriptor === "string" ? JSON.parse(descriptor) : descriptor;
|
|
23662
|
+
const cwd = parsed?.payload?.cwd;
|
|
23663
|
+
if (cwd === undefined)
|
|
23664
|
+
return root;
|
|
23665
|
+
if (typeof cwd !== "string" || !path8.isAbsolute(cwd))
|
|
23666
|
+
return null;
|
|
23667
|
+
return realpathSync3(cwd);
|
|
23668
|
+
} catch {
|
|
23669
|
+
return null;
|
|
23670
|
+
}
|
|
23671
|
+
};
|
|
23672
|
+
var standingApprovalLive = (root, receipt, cls, dir, descriptor) => {
|
|
23093
23673
|
if (!isStandingReceipt(receipt))
|
|
23094
23674
|
return false;
|
|
23095
23675
|
if (typeof cls !== "string" || !AUTO_CLASSES.includes(cls))
|
|
23096
23676
|
return false;
|
|
23097
23677
|
if (receipt.class !== cls)
|
|
23098
23678
|
return false;
|
|
23099
|
-
const
|
|
23679
|
+
const target = targetOf(root, descriptor);
|
|
23680
|
+
if (!target)
|
|
23681
|
+
return false;
|
|
23682
|
+
const resolved = resolveAutoApproval(target, dir);
|
|
23100
23683
|
if (resolved.status !== "on")
|
|
23101
23684
|
return false;
|
|
23102
23685
|
if (resolved.workspace !== receipt.workspace)
|
|
@@ -23115,9 +23698,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
|
23115
23698
|
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
23116
23699
|
if (task.status !== "active")
|
|
23117
23700
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
23118
|
-
const
|
|
23119
|
-
if (!
|
|
23120
|
-
return
|
|
23701
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
23702
|
+
if (!workspace.ok)
|
|
23703
|
+
return workspace;
|
|
23704
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
23705
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
23121
23706
|
const standing = binding.standing;
|
|
23122
23707
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
23123
23708
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -23130,16 +23715,19 @@ var verifyStandingApproval = (root, task, caller, binding) => {
|
|
|
23130
23715
|
const cls = operationAutoClass(operation);
|
|
23131
23716
|
if (!cls || cls !== standing.class)
|
|
23132
23717
|
return failure("permission_denied", "standing approval does not cover this operation");
|
|
23133
|
-
const
|
|
23718
|
+
const target = targetOf(root, binding.approvedContent);
|
|
23719
|
+
if (!target)
|
|
23720
|
+
return failure("permission_denied", "standing action target is invalid");
|
|
23721
|
+
const resolved = resolveAutoApproval(target);
|
|
23134
23722
|
if (resolved.status !== "on" || resolved.workspace !== standing.workspace || !resolved.classes.includes(cls))
|
|
23135
23723
|
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
23136
23724
|
if (cls === "push") {
|
|
23137
|
-
const workspace = resolveWorkspaceFrom(
|
|
23725
|
+
const workspace = resolveWorkspaceFrom(target, configDir());
|
|
23138
23726
|
const provider = workspace?.vcs?.provider;
|
|
23139
23727
|
const account = resolved.account;
|
|
23140
23728
|
if (!provider || !account)
|
|
23141
23729
|
return failure("permission_denied", "auto-push requires a workspace provider and area account");
|
|
23142
|
-
const identity = verifyPushIdentity(
|
|
23730
|
+
const identity = verifyPushIdentity(target, provider, account);
|
|
23143
23731
|
if (!identity.ok)
|
|
23144
23732
|
return failure("permission_denied", identity.error);
|
|
23145
23733
|
}
|
|
@@ -23148,7 +23736,7 @@ var verifyStandingApproval = (root, task, caller, binding) => {
|
|
|
23148
23736
|
host: caller.host,
|
|
23149
23737
|
session: { kind: "host", host: caller.host, handle: caller.actor },
|
|
23150
23738
|
workerId: null,
|
|
23151
|
-
receipts: [standingReceiptFor(
|
|
23739
|
+
receipts: [standingReceiptFor(target, standing.workspace, cls)]
|
|
23152
23740
|
});
|
|
23153
23741
|
};
|
|
23154
23742
|
|
|
@@ -23299,7 +23887,7 @@ function applicableDecision(task, purpose, binding, checkoutRoot) {
|
|
|
23299
23887
|
return [];
|
|
23300
23888
|
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);
|
|
23301
23889
|
}
|
|
23302
|
-
var digestBytes2 = (bytes) =>
|
|
23890
|
+
var digestBytes2 = (bytes) => createHash5("sha256").update(bytes).digest("hex");
|
|
23303
23891
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
23304
23892
|
let root;
|
|
23305
23893
|
try {
|
|
@@ -23312,23 +23900,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
23312
23900
|
continue;
|
|
23313
23901
|
if (!reference.digest)
|
|
23314
23902
|
return failure("invalid_input", "document references require a byte digest");
|
|
23315
|
-
const target =
|
|
23316
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
23903
|
+
const target = path9.resolve(root, reference.path);
|
|
23904
|
+
if (target !== root && !target.startsWith(`${root}${path9.sep}`))
|
|
23317
23905
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
23318
23906
|
fields: [
|
|
23319
23907
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
23320
23908
|
]
|
|
23321
23909
|
});
|
|
23322
23910
|
try {
|
|
23323
|
-
const relative =
|
|
23911
|
+
const relative = path9.relative(root, target);
|
|
23324
23912
|
let current = root;
|
|
23325
|
-
for (const segment of relative.split(
|
|
23913
|
+
for (const segment of relative.split(path9.sep)) {
|
|
23326
23914
|
if (!segment)
|
|
23327
23915
|
continue;
|
|
23328
|
-
current =
|
|
23916
|
+
current = path9.join(current, segment);
|
|
23329
23917
|
const stat = fs6.lstatSync(current);
|
|
23330
23918
|
const real = fs6.realpathSync(current);
|
|
23331
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
23919
|
+
if (real !== root && !real.startsWith(`${root}${path9.sep}`))
|
|
23332
23920
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
23333
23921
|
fields: [
|
|
23334
23922
|
{
|
|
@@ -23354,6 +23942,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
23354
23942
|
};
|
|
23355
23943
|
var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
|
|
23356
23944
|
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);
|
|
23945
|
+
var approvalAuthorityMatches = (store, task, approval, current) => {
|
|
23946
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
23947
|
+
return false;
|
|
23948
|
+
if (provenanceMatches(approval, current))
|
|
23949
|
+
return true;
|
|
23950
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
23951
|
+
if (!session || session.kind !== "host")
|
|
23952
|
+
return false;
|
|
23953
|
+
const workspace = store.readWorkspace();
|
|
23954
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
23955
|
+
host: session.host,
|
|
23956
|
+
actor: session.handle
|
|
23957
|
+
});
|
|
23958
|
+
};
|
|
23357
23959
|
var validateAction = (store, task, workspaceId, input, authority) => {
|
|
23358
23960
|
if (task.status !== "active")
|
|
23359
23961
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
@@ -23379,7 +23981,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
23379
23981
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
23380
23982
|
if (decision.response !== "approved")
|
|
23381
23983
|
return failure("permission_denied", "decision was rejected");
|
|
23382
|
-
if (
|
|
23984
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
23383
23985
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
23384
23986
|
const receipts = entry.provenance.receipts;
|
|
23385
23987
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -23392,7 +23994,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
23392
23994
|
operation = null;
|
|
23393
23995
|
}
|
|
23394
23996
|
const cls = operationAutoClass(operation);
|
|
23395
|
-
if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls)))
|
|
23997
|
+
if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls, undefined, decision.binding.approvedContent)))
|
|
23396
23998
|
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
23397
23999
|
}
|
|
23398
24000
|
if (decision.revoked)
|
|
@@ -23546,7 +24148,7 @@ function settleAction(input) {
|
|
|
23546
24148
|
return failure("not_found", "decision not found");
|
|
23547
24149
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
23548
24150
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
23549
|
-
if (!
|
|
24151
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
23550
24152
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
23551
24153
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
23552
24154
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -23645,7 +24247,7 @@ function reconcileAction(input) {
|
|
|
23645
24247
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
23646
24248
|
if (!entry || entry.data.purpose !== "action")
|
|
23647
24249
|
return failure("not_found", "decision not found");
|
|
23648
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
24250
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
23649
24251
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
23650
24252
|
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)
|
|
23651
24253
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -23694,15 +24296,15 @@ var bindingCovers = scopeCovers;
|
|
|
23694
24296
|
var verifyDecisionContent = verifyContentRefs;
|
|
23695
24297
|
|
|
23696
24298
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
23697
|
-
var digestBytes3 = (value) =>
|
|
23698
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
23699
|
-
var relative = (root, candidate) =>
|
|
24299
|
+
var digestBytes3 = (value) => createHash6("sha256").update(value).digest("hex");
|
|
24300
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path10.sep}`);
|
|
24301
|
+
var relative = (root, candidate) => path10.relative(root, candidate).split(path10.sep).join("/") || ".";
|
|
23700
24302
|
var canonicalPath = (value) => {
|
|
23701
24303
|
if (typeof value !== "string" || !value)
|
|
23702
24304
|
return null;
|
|
23703
24305
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
23704
24306
|
return null;
|
|
23705
|
-
const normalized =
|
|
24307
|
+
const normalized = path10.posix.normalize(value);
|
|
23706
24308
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
23707
24309
|
return null;
|
|
23708
24310
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -23750,14 +24352,14 @@ var gitPaths = (root) => {
|
|
|
23750
24352
|
git: /not a git repository/i.test(stderr) ? false : true
|
|
23751
24353
|
};
|
|
23752
24354
|
}
|
|
23753
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24355
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")) : [];
|
|
23754
24356
|
const stagedDeleted = spawnSync2("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
23755
24357
|
if (stagedDeleted.status !== 0) {
|
|
23756
24358
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
23757
24359
|
if (!/not a git repository/i.test(stderr))
|
|
23758
24360
|
return { paths, uncertain: true, git: true };
|
|
23759
24361
|
} else if (stagedDeleted.stdout) {
|
|
23760
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24362
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")));
|
|
23761
24363
|
}
|
|
23762
24364
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
23763
24365
|
};
|
|
@@ -23772,7 +24374,7 @@ var walk = (root, directory, output) => {
|
|
|
23772
24374
|
for (const name of names) {
|
|
23773
24375
|
if (name === ".git" || name === ".workit")
|
|
23774
24376
|
continue;
|
|
23775
|
-
const target =
|
|
24377
|
+
const target = path10.join(directory, name);
|
|
23776
24378
|
let stat;
|
|
23777
24379
|
try {
|
|
23778
24380
|
stat = fs7.lstatSync(target);
|
|
@@ -23790,12 +24392,12 @@ var walk = (root, directory, output) => {
|
|
|
23790
24392
|
var scopeRoots = (root, scope) => {
|
|
23791
24393
|
const roots = [];
|
|
23792
24394
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
23793
|
-
const target =
|
|
24395
|
+
const target = path10.resolve(root, item);
|
|
23794
24396
|
if (!inside(root, target))
|
|
23795
24397
|
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` }] });
|
|
23796
24398
|
let ancestor = target;
|
|
23797
24399
|
while (!fs7.existsSync(ancestor) && ancestor !== root)
|
|
23798
|
-
ancestor =
|
|
24400
|
+
ancestor = path10.dirname(ancestor);
|
|
23799
24401
|
try {
|
|
23800
24402
|
if (!inside(root, fs7.realpathSync(ancestor)))
|
|
23801
24403
|
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` }] });
|
|
@@ -23847,7 +24449,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23847
24449
|
for (const item of [...names].sort()) {
|
|
23848
24450
|
if (!scopeMatches(item, normalizedScope))
|
|
23849
24451
|
continue;
|
|
23850
|
-
const target =
|
|
24452
|
+
const target = path10.join(checkout, item);
|
|
23851
24453
|
try {
|
|
23852
24454
|
const stat = fs7.lstatSync(target);
|
|
23853
24455
|
if (stat.isSymbolicLink()) {
|
|
@@ -23982,7 +24584,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
|
23982
24584
|
candidateId: evidence.candidateId ?? fallback
|
|
23983
24585
|
};
|
|
23984
24586
|
};
|
|
23985
|
-
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
24587
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
|
|
23986
24588
|
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")));
|
|
23987
24589
|
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 }));
|
|
23988
24590
|
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
@@ -24217,6 +24819,11 @@ var exportDigest = (bundle) => sha256({
|
|
|
24217
24819
|
task: bundle.task
|
|
24218
24820
|
});
|
|
24219
24821
|
// packages/workit-core/src/core/external-action.ts
|
|
24822
|
+
var chainStepSchema = union3([
|
|
24823
|
+
string5().min(1),
|
|
24824
|
+
object4({ branch: string5().min(1) }).strict(),
|
|
24825
|
+
object4({ pr: literal3(true) }).strict()
|
|
24826
|
+
]);
|
|
24220
24827
|
var externalActionSchema = discriminatedUnion2("operation", [
|
|
24221
24828
|
object4({
|
|
24222
24829
|
operation: literal3("git.branch_setup"),
|
|
@@ -24224,20 +24831,22 @@ var externalActionSchema = discriminatedUnion2("operation", [
|
|
|
24224
24831
|
action: _enum2(["setup", "reapply_stash"]).optional(),
|
|
24225
24832
|
sdd_dir: string5().optional(),
|
|
24226
24833
|
target_branch: string5().optional(),
|
|
24227
|
-
stash: _enum2(["yes", "no"]).optional()
|
|
24834
|
+
stash: _enum2(["yes", "no"]).optional(),
|
|
24835
|
+
cwd: string5().min(1).optional()
|
|
24228
24836
|
}).strict()
|
|
24229
24837
|
}).strict(),
|
|
24230
24838
|
object4({
|
|
24231
24839
|
operation: literal3("git.commit"),
|
|
24232
24840
|
payload: object4({
|
|
24233
24841
|
message: string5().min(1).optional(),
|
|
24234
|
-
plan_steps: array3(
|
|
24235
|
-
plan_branch: string5().min(1).optional()
|
|
24842
|
+
plan_steps: array3(chainStepSchema).min(1).max(32).optional(),
|
|
24843
|
+
plan_branch: string5().min(1).optional(),
|
|
24844
|
+
cwd: string5().min(1).optional()
|
|
24236
24845
|
}).strict()
|
|
24237
24846
|
}).strict(),
|
|
24238
24847
|
object4({
|
|
24239
24848
|
operation: literal3("git.push"),
|
|
24240
|
-
payload: object4({ branch: string5().min(1).optional() }).strict()
|
|
24849
|
+
payload: object4({ branch: string5().min(1).optional(), cwd: string5().min(1).optional() }).strict()
|
|
24241
24850
|
}).strict(),
|
|
24242
24851
|
object4({
|
|
24243
24852
|
operation: literal3("hosting.pull_request"),
|
|
@@ -24246,16 +24855,22 @@ var externalActionSchema = discriminatedUnion2("operation", [
|
|
|
24246
24855
|
body: string5().optional(),
|
|
24247
24856
|
draft: boolean5().optional(),
|
|
24248
24857
|
target_branch: string5().optional(),
|
|
24249
|
-
babysit: boolean5().optional()
|
|
24858
|
+
babysit: boolean5().optional(),
|
|
24859
|
+
cwd: string5().min(1).optional()
|
|
24250
24860
|
}).strict()
|
|
24251
24861
|
}).strict(),
|
|
24252
24862
|
object4({
|
|
24253
24863
|
operation: literal3("hosting.merge"),
|
|
24254
24864
|
payload: object4({
|
|
24255
24865
|
target_branch: string5().min(1).optional(),
|
|
24256
|
-
source_branch: string5().min(1).optional()
|
|
24866
|
+
source_branch: string5().min(1).optional(),
|
|
24867
|
+
cwd: string5().min(1).optional()
|
|
24257
24868
|
}).strict()
|
|
24258
24869
|
}).strict(),
|
|
24870
|
+
object4({
|
|
24871
|
+
operation: literal3("hosting.delete_branch"),
|
|
24872
|
+
payload: object4({ branch: string5().min(1), cwd: string5().min(1).optional() }).strict()
|
|
24873
|
+
}).strict(),
|
|
24259
24874
|
object4({
|
|
24260
24875
|
operation: literal3("youtrack.update"),
|
|
24261
24876
|
payload: object4({
|
|
@@ -24311,12 +24926,13 @@ var externalActionSchema = discriminatedUnion2("operation", [
|
|
|
24311
24926
|
issueRef: string5().optional(),
|
|
24312
24927
|
mode: string5().optional(),
|
|
24313
24928
|
specPath: string5().optional(),
|
|
24314
|
-
planPath: string5().optional()
|
|
24929
|
+
planPath: string5().optional(),
|
|
24930
|
+
cwd: string5().min(1).optional()
|
|
24315
24931
|
}).strict()
|
|
24316
24932
|
}).strict()
|
|
24317
24933
|
]);
|
|
24318
24934
|
// packages/workit-core/src/core/task-engine.ts
|
|
24319
|
-
import { realpathSync as
|
|
24935
|
+
import { realpathSync as realpathSync6 } from "node:fs";
|
|
24320
24936
|
|
|
24321
24937
|
// packages/workit-core/src/core/policy-resolver.ts
|
|
24322
24938
|
var resolverInputSchema = object4({
|
|
@@ -24717,6 +25333,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
24717
25333
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
24718
25334
|
state: expected.state,
|
|
24719
25335
|
session: expected.session,
|
|
25336
|
+
report: expected.report ?? null,
|
|
24720
25337
|
provenance: result.data,
|
|
24721
25338
|
...binding
|
|
24722
25339
|
});
|
|
@@ -24727,7 +25344,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
24727
25344
|
verifiedWorkers.delete(token);
|
|
24728
25345
|
if (!value)
|
|
24729
25346
|
return null;
|
|
24730
|
-
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))
|
|
25347
|
+
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))
|
|
24731
25348
|
return null;
|
|
24732
25349
|
return value;
|
|
24733
25350
|
};
|
|
@@ -24768,6 +25385,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
24768
25385
|
return failure("invalid_transition", "worker already has an observed session");
|
|
24769
25386
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
24770
25387
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
25388
|
+
if (input.report && input.state !== "stopped")
|
|
25389
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
24771
25390
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
24772
25391
|
return failure("invalid_input", "running worker observations require a session");
|
|
24773
25392
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -24784,12 +25403,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
24784
25403
|
data: {
|
|
24785
25404
|
...entry.data,
|
|
24786
25405
|
state: terminalCancel ? "cancelling" : input.state,
|
|
24787
|
-
session: input.session
|
|
25406
|
+
session: input.session,
|
|
25407
|
+
report: entry.data.report ?? input.report ?? null
|
|
24788
25408
|
}
|
|
24789
25409
|
};
|
|
24790
25410
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
24791
25411
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
24792
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
25412
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
24793
25413
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
24794
25414
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
24795
25415
|
taskId: task.data.id,
|
|
@@ -24857,6 +25477,18 @@ var portableTask = (task) => {
|
|
|
24857
25477
|
};
|
|
24858
25478
|
for (const entry of clone.assessments)
|
|
24859
25479
|
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
25480
|
+
const sourceDecisions = new Map(task.decisions.map((entry) => [entry.id, entry]));
|
|
25481
|
+
for (const entry of clone.decisions) {
|
|
25482
|
+
const consumption = sourceDecisions.get(entry.id)?.data.consumption;
|
|
25483
|
+
if (!consumption)
|
|
25484
|
+
continue;
|
|
25485
|
+
entry.data.consumption = {
|
|
25486
|
+
...consumption,
|
|
25487
|
+
state: consumption.state === "reserved" ? "uncertain" : consumption.state,
|
|
25488
|
+
actionRef: { kind: "record", collection: "decisions", id: entry.id },
|
|
25489
|
+
...consumption.historicalActionId ? { historicalActionId: consumption.historicalActionId } : consumption.actionRef.kind === "host" ? { historicalActionId: consumption.actionRef.handle } : {}
|
|
25490
|
+
};
|
|
25491
|
+
}
|
|
24860
25492
|
clone.candidates = [];
|
|
24861
25493
|
return clone;
|
|
24862
25494
|
};
|
|
@@ -24982,7 +25614,11 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
24982
25614
|
taskId: "",
|
|
24983
25615
|
workspaceId: destinationWorkspaceId
|
|
24984
25616
|
},
|
|
24985
|
-
consumption:
|
|
25617
|
+
consumption: data.consumption ? {
|
|
25618
|
+
...data.consumption,
|
|
25619
|
+
state: data.consumption.state === "reserved" ? "uncertain" : data.consumption.state,
|
|
25620
|
+
actionRef: { kind: "record", collection: "decisions", id: entry.id }
|
|
25621
|
+
} : null
|
|
24986
25622
|
}
|
|
24987
25623
|
};
|
|
24988
25624
|
}),
|
|
@@ -25023,7 +25659,7 @@ class WorkitCore {
|
|
|
25023
25659
|
}
|
|
25024
25660
|
contextRootError() {
|
|
25025
25661
|
try {
|
|
25026
|
-
if (
|
|
25662
|
+
if (realpathSync6(this.context.root) !== this.store.root)
|
|
25027
25663
|
return failure("invalid_input", "operation context root does not match the task store root");
|
|
25028
25664
|
} catch {
|
|
25029
25665
|
return failure("invalid_input", "operation context root cannot be resolved");
|
|
@@ -25182,7 +25818,7 @@ class WorkitCore {
|
|
|
25182
25818
|
return parsed;
|
|
25183
25819
|
const input = parsed.data;
|
|
25184
25820
|
if ((this.context.workerId ?? null) !== null) {
|
|
25185
|
-
if (input.action !== "inspect")
|
|
25821
|
+
if (input.action !== "inspect" && input.action !== "list")
|
|
25186
25822
|
return failure("permission_denied", "helpers cannot control task lifecycle or scope");
|
|
25187
25823
|
}
|
|
25188
25824
|
switch (input.action) {
|
|
@@ -25207,14 +25843,45 @@ class WorkitCore {
|
|
|
25207
25843
|
const listed = this.store.listTasks();
|
|
25208
25844
|
if (!listed.ok)
|
|
25209
25845
|
return listed;
|
|
25210
|
-
const
|
|
25211
|
-
|
|
25212
|
-
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
|
|
25216
|
-
|
|
25217
|
-
|
|
25846
|
+
const status = input.status ?? "open";
|
|
25847
|
+
const limit = input.limit ?? 20;
|
|
25848
|
+
const query = input.query?.toLowerCase().split(/\s+/);
|
|
25849
|
+
const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").filter((task) => !query || query.every((term) => JSON.stringify({
|
|
25850
|
+
id: task.id,
|
|
25851
|
+
workspaceId: task.workspaceId,
|
|
25852
|
+
origin: task.origin,
|
|
25853
|
+
createdAt: task.createdAt,
|
|
25854
|
+
updatedAt: task.updatedAt,
|
|
25855
|
+
source: task.intent.provenance,
|
|
25856
|
+
objective: task.intent.data.objective,
|
|
25857
|
+
progress: task.progress,
|
|
25858
|
+
decisions: task.decisions.map(({ data }) => data)
|
|
25859
|
+
}).toLowerCase().includes(term))).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
25860
|
+
if (selected.length === 0)
|
|
25861
|
+
return success(null, null, []);
|
|
25862
|
+
const workspace = this.store.readWorkspace();
|
|
25863
|
+
if (!workspace.ok)
|
|
25864
|
+
return workspace;
|
|
25865
|
+
if (!workspace.data)
|
|
25866
|
+
return failure("not_found", "workspace not found");
|
|
25867
|
+
const tasks = selected.map((task) => ({
|
|
25868
|
+
id: task.id,
|
|
25869
|
+
revision: task.revision,
|
|
25870
|
+
workspaceRevision: workspace.data.revision,
|
|
25871
|
+
createdAt: task.createdAt,
|
|
25872
|
+
updatedAt: task.updatedAt,
|
|
25873
|
+
runtime: task.runtime ?? null,
|
|
25874
|
+
objective: task.intent.data.objective,
|
|
25875
|
+
status: task.status,
|
|
25876
|
+
closure: task.closure,
|
|
25877
|
+
progress: task.progress,
|
|
25878
|
+
writer: task.status === "closed" ? null : workspace.data.writer,
|
|
25879
|
+
source: {
|
|
25880
|
+
host: task.intent.provenance.host,
|
|
25881
|
+
kind: task.intent.provenance.kind
|
|
25882
|
+
}
|
|
25883
|
+
}));
|
|
25884
|
+
return success(null, null, tasks);
|
|
25218
25885
|
}
|
|
25219
25886
|
case "inspect": {
|
|
25220
25887
|
const task = this.store.readTask(input.taskId);
|
|
@@ -25223,7 +25890,7 @@ class WorkitCore {
|
|
|
25223
25890
|
const helper = this.helperTaskGuard(task.data, true);
|
|
25224
25891
|
if (!helper.ok)
|
|
25225
25892
|
return helper;
|
|
25226
|
-
if (input.view === "summary")
|
|
25893
|
+
if ((input.view ?? "summary") === "summary")
|
|
25227
25894
|
return this.summary(task.data);
|
|
25228
25895
|
return this.view(task.data);
|
|
25229
25896
|
}
|
|
@@ -25567,7 +26234,7 @@ class WorkitCore {
|
|
|
25567
26234
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
25568
26235
|
const verified = evidence.some((entry) => {
|
|
25569
26236
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
25570
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
26237
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
25571
26238
|
kind: entry.data.kind,
|
|
25572
26239
|
candidateId: entry.data.candidateId,
|
|
25573
26240
|
status: evaluation?.status ?? "missing"
|
|
@@ -25647,7 +26314,18 @@ class WorkitCore {
|
|
|
25647
26314
|
if (task.data.status !== "active")
|
|
25648
26315
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
25649
26316
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
25650
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
26317
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
26318
|
+
fields: [
|
|
26319
|
+
{
|
|
26320
|
+
path: "assignment.scope",
|
|
26321
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
26322
|
+
},
|
|
26323
|
+
{
|
|
26324
|
+
path: "task.intent.scope",
|
|
26325
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
26326
|
+
}
|
|
26327
|
+
]
|
|
26328
|
+
});
|
|
25651
26329
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
25652
26330
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
25653
26331
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -25854,7 +26532,15 @@ class WorkitCore {
|
|
|
25854
26532
|
...item,
|
|
25855
26533
|
recordedAt: mutation.now,
|
|
25856
26534
|
provenance: attested.data,
|
|
25857
|
-
data: {
|
|
26535
|
+
data: {
|
|
26536
|
+
...item.data,
|
|
26537
|
+
state: "dispatching",
|
|
26538
|
+
coordinator: {
|
|
26539
|
+
kind: "host",
|
|
26540
|
+
host: this.context.caller.host,
|
|
26541
|
+
handle: this.context.caller.actor
|
|
26542
|
+
}
|
|
26543
|
+
}
|
|
25858
26544
|
} : item)
|
|
25859
26545
|
});
|
|
25860
26546
|
}
|
|
@@ -25930,6 +26616,7 @@ class WorkitCore {
|
|
|
25930
26616
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
25931
26617
|
state: "stopped",
|
|
25932
26618
|
session: null,
|
|
26619
|
+
report: null,
|
|
25933
26620
|
provenance: attested.data,
|
|
25934
26621
|
owner: this.authorityOwner,
|
|
25935
26622
|
store: this.store,
|
|
@@ -26237,10 +26924,12 @@ class WorkitCore {
|
|
|
26237
26924
|
return workspace;
|
|
26238
26925
|
if (!workspace.data)
|
|
26239
26926
|
return failure("not_found", "workspace not found");
|
|
26240
|
-
const
|
|
26927
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
26928
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
26241
26929
|
if (!current.ok)
|
|
26242
26930
|
return current;
|
|
26243
|
-
const
|
|
26931
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
26932
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
26244
26933
|
return success(task.revision, workspace.data.revision, {
|
|
26245
26934
|
id: task.id,
|
|
26246
26935
|
revision: task.revision,
|
|
@@ -26254,7 +26943,7 @@ class WorkitCore {
|
|
|
26254
26943
|
progress: task.progress,
|
|
26255
26944
|
policy: task.policy,
|
|
26256
26945
|
requirements,
|
|
26257
|
-
writer:
|
|
26946
|
+
writer: evaluationWorkspace.writer
|
|
26258
26947
|
});
|
|
26259
26948
|
}
|
|
26260
26949
|
view(task) {
|
|
@@ -26263,13 +26952,15 @@ class WorkitCore {
|
|
|
26263
26952
|
return workspace;
|
|
26264
26953
|
if (!workspace.data)
|
|
26265
26954
|
return failure("not_found", "workspace not found");
|
|
26266
|
-
const
|
|
26955
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
26956
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
26267
26957
|
if (!current.ok)
|
|
26268
26958
|
return current;
|
|
26269
|
-
const
|
|
26959
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
26960
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
26270
26961
|
return success(task.revision, workspace.data.revision, {
|
|
26271
26962
|
task,
|
|
26272
|
-
workspace:
|
|
26963
|
+
workspace: evaluationWorkspace,
|
|
26273
26964
|
capabilities: this.context.capabilities,
|
|
26274
26965
|
requirements,
|
|
26275
26966
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -26284,7 +26975,7 @@ class WorkitCore {
|
|
|
26284
26975
|
if (view.task.workspaceId !== view.workspace.id)
|
|
26285
26976
|
return failure("invalid_input", "task and workspace bindings are invalid");
|
|
26286
26977
|
try {
|
|
26287
|
-
if (
|
|
26978
|
+
if (realpathSync6(view.workspace.root) !== this.store.root)
|
|
26288
26979
|
return failure("invalid_input", "task view root does not match the task store");
|
|
26289
26980
|
} catch {
|
|
26290
26981
|
return failure("invalid_input", "task view root cannot be resolved");
|
|
@@ -26360,8 +27051,6 @@ class WorkitCore {
|
|
|
26360
27051
|
return helper;
|
|
26361
27052
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
26362
27053
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
26363
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
26364
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
26365
27054
|
const workspace = this.store.readWorkspace();
|
|
26366
27055
|
if (!workspace.ok)
|
|
26367
27056
|
return workspace;
|
|
@@ -26391,7 +27080,7 @@ class WorkitCore {
|
|
|
26391
27080
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
26392
27081
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
26393
27082
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
26394
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
27083
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
26395
27084
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
26396
27085
|
resumeCandidate = current.data.candidate;
|
|
26397
27086
|
}
|
|
@@ -26521,7 +27210,7 @@ class WorkitCore {
|
|
|
26521
27210
|
task: (value, context) => success(context.revision, null, {
|
|
26522
27211
|
...value,
|
|
26523
27212
|
status: "closed",
|
|
26524
|
-
candidates:
|
|
27213
|
+
candidates: taskForView.candidates,
|
|
26525
27214
|
closure: {
|
|
26526
27215
|
outcome: closure.data.outcome,
|
|
26527
27216
|
at: context.now,
|
|
@@ -26563,22 +27252,22 @@ function resolveGitRevision(root, value) {
|
|
|
26563
27252
|
}
|
|
26564
27253
|
}
|
|
26565
27254
|
function resolveInside(root, candidate) {
|
|
26566
|
-
const base =
|
|
26567
|
-
const target =
|
|
26568
|
-
if (target !== base && !target.startsWith(base +
|
|
27255
|
+
const base = realpathSync7(root);
|
|
27256
|
+
const target = path11.resolve(base, candidate);
|
|
27257
|
+
if (target !== base && !target.startsWith(base + path11.sep)) {
|
|
26569
27258
|
throw new Error("path must stay inside repository root");
|
|
26570
27259
|
}
|
|
26571
27260
|
let ancestor = target;
|
|
26572
27261
|
while (!existsSync5(ancestor))
|
|
26573
|
-
ancestor =
|
|
26574
|
-
const canonicalAncestor =
|
|
26575
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
27262
|
+
ancestor = path11.dirname(ancestor);
|
|
27263
|
+
const canonicalAncestor = realpathSync7(ancestor);
|
|
27264
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path11.sep)) {
|
|
26576
27265
|
throw new Error("path must stay inside repository root");
|
|
26577
27266
|
}
|
|
26578
27267
|
return target;
|
|
26579
27268
|
}
|
|
26580
27269
|
function run2(root, executable, args, env = {}) {
|
|
26581
|
-
const cwd =
|
|
27270
|
+
const cwd = realpathSync7(root);
|
|
26582
27271
|
const result = spawnSync3(executable, args, {
|
|
26583
27272
|
cwd,
|
|
26584
27273
|
encoding: "utf8",
|
|
@@ -26651,18 +27340,19 @@ var redactSecrets = (value) => applyPatterns(value);
|
|
|
26651
27340
|
|
|
26652
27341
|
// packages/workit-core/src/core/pr-create.ts
|
|
26653
27342
|
import fs8 from "node:fs";
|
|
26654
|
-
import
|
|
27343
|
+
import path12 from "node:path";
|
|
27344
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
26655
27345
|
function parseGhIssue(value) {
|
|
26656
27346
|
const m = /issues\/(\d+)/.exec(value);
|
|
26657
27347
|
return m ? m[1] : String(value).trim().replace(/^#/, "");
|
|
26658
27348
|
}
|
|
26659
27349
|
function whichOnPath(tool) {
|
|
26660
27350
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
26661
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
27351
|
+
for (const dir of (process.env.PATH ?? "").split(path12.delimiter)) {
|
|
26662
27352
|
if (!dir)
|
|
26663
27353
|
continue;
|
|
26664
27354
|
for (const name of names) {
|
|
26665
|
-
const candidate =
|
|
27355
|
+
const candidate = path12.join(dir, name);
|
|
26666
27356
|
try {
|
|
26667
27357
|
fs8.accessSync(candidate, fs8.constants.X_OK);
|
|
26668
27358
|
return candidate;
|
|
@@ -26672,11 +27362,21 @@ function whichOnPath(tool) {
|
|
|
26672
27362
|
return null;
|
|
26673
27363
|
}
|
|
26674
27364
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
27365
|
+
var pushRemote = (cwd) => {
|
|
27366
|
+
const result = spawnSync4("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
27367
|
+
cwd,
|
|
27368
|
+
encoding: "utf8"
|
|
27369
|
+
});
|
|
27370
|
+
if (result.status !== 0)
|
|
27371
|
+
return null;
|
|
27372
|
+
const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
|
|
27373
|
+
return urls.length === 1 ? urls[0] : null;
|
|
27374
|
+
};
|
|
26675
27375
|
|
|
26676
27376
|
// packages/workit-core/src/core/repo-context.ts
|
|
26677
|
-
import { spawnSync as
|
|
27377
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
26678
27378
|
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
26679
|
-
import
|
|
27379
|
+
import path13 from "node:path";
|
|
26680
27380
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
26681
27381
|
try {
|
|
26682
27382
|
gitRevisionParts(value);
|
|
@@ -26703,7 +27403,7 @@ var invalidRange = (cwd) => ({
|
|
|
26703
27403
|
cwd
|
|
26704
27404
|
});
|
|
26705
27405
|
var runGit = (cwd, args) => {
|
|
26706
|
-
const result =
|
|
27406
|
+
const result = spawnSync5("git", args, { cwd, encoding: "utf8" });
|
|
26707
27407
|
return {
|
|
26708
27408
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
26709
27409
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -26810,7 +27510,7 @@ var findPrTemplate = (cwd) => {
|
|
|
26810
27510
|
]) {
|
|
26811
27511
|
const parts = rel.split("/");
|
|
26812
27512
|
const base = parts[parts.length - 1];
|
|
26813
|
-
const dir = parts.length > 1 ?
|
|
27513
|
+
const dir = parts.length > 1 ? path13.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
26814
27514
|
const actual = probeCaseInsensitive(dir, base);
|
|
26815
27515
|
if (actual)
|
|
26816
27516
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -26841,7 +27541,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
26841
27541
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
26842
27542
|
};
|
|
26843
27543
|
var packageScripts = (cwd, keys) => {
|
|
26844
|
-
const pkgPath =
|
|
27544
|
+
const pkgPath = path13.join(cwd, "package.json");
|
|
26845
27545
|
if (!existsSync6(pkgPath))
|
|
26846
27546
|
return [];
|
|
26847
27547
|
try {
|
|
@@ -26867,9 +27567,9 @@ var documentationFiles = (cwd) => {
|
|
|
26867
27567
|
if (entry.isDirectory()) {
|
|
26868
27568
|
if (excluded.has(entry.name))
|
|
26869
27569
|
continue;
|
|
26870
|
-
walk(
|
|
27570
|
+
walk(path13.join(dir, entry.name), depth + 1);
|
|
26871
27571
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
26872
|
-
matches.push("./" +
|
|
27572
|
+
matches.push("./" + path13.relative(cwd, path13.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
26873
27573
|
}
|
|
26874
27574
|
}
|
|
26875
27575
|
};
|
|
@@ -26885,7 +27585,7 @@ var readTrimmed = (file, maxLines) => {
|
|
|
26885
27585
|
`);
|
|
26886
27586
|
};
|
|
26887
27587
|
function prReadyContext(root, range) {
|
|
26888
|
-
const cwd =
|
|
27588
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
26889
27589
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
26890
27590
|
return invalidRange(cwd);
|
|
26891
27591
|
let stdout = "";
|
|
@@ -26943,7 +27643,7 @@ ${status.stderr}` : "");
|
|
|
26943
27643
|
stdout += `template_path: ${template}
|
|
26944
27644
|
|
|
26945
27645
|
`;
|
|
26946
|
-
stdout += readTrimmed(
|
|
27646
|
+
stdout += readTrimmed(path13.join(cwd, template), 220);
|
|
26947
27647
|
} else {
|
|
26948
27648
|
stdout += `template_path: none
|
|
26949
27649
|
|
|
@@ -26953,7 +27653,7 @@ ${status.stderr}` : "");
|
|
|
26953
27653
|
stdout += `
|
|
26954
27654
|
`;
|
|
26955
27655
|
stdout += printSection("Recent Validation Signals");
|
|
26956
|
-
if (existsSync6(
|
|
27656
|
+
if (existsSync6(path13.join(cwd, "package.json"))) {
|
|
26957
27657
|
stdout += `package.json detected. Common scripts:
|
|
26958
27658
|
`;
|
|
26959
27659
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -26962,7 +27662,7 @@ ${status.stderr}` : "");
|
|
|
26962
27662
|
`) + `
|
|
26963
27663
|
`;
|
|
26964
27664
|
}
|
|
26965
|
-
if (existsSync6(
|
|
27665
|
+
if (existsSync6(path13.join(cwd, "Cargo.toml")) || existsSync6(path13.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
26966
27666
|
stdout += `Rust project detected.
|
|
26967
27667
|
`;
|
|
26968
27668
|
}
|
|
@@ -26995,7 +27695,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
26995
27695
|
- Apply with the native workit_changelog_apply tool only (not hand-edits under Unreleased).
|
|
26996
27696
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
26997
27697
|
function changelogContext(root, range) {
|
|
26998
|
-
const cwd =
|
|
27698
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
26999
27699
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27000
27700
|
return invalidRange(cwd);
|
|
27001
27701
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27013,7 +27713,7 @@ function changelogContext(root, range) {
|
|
|
27013
27713
|
stdout += CHANGELOG_RULES + `
|
|
27014
27714
|
`;
|
|
27015
27715
|
stdout += printSection("Existing CHANGELOG.md");
|
|
27016
|
-
const changelogPath =
|
|
27716
|
+
const changelogPath = path13.join(cwd, "CHANGELOG.md");
|
|
27017
27717
|
if (existsSync6(changelogPath)) {
|
|
27018
27718
|
stdout += readTrimmed(changelogPath, 260) + `
|
|
27019
27719
|
`;
|
|
@@ -27033,7 +27733,7 @@ function changelogContext(root, range) {
|
|
|
27033
27733
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27034
27734
|
}
|
|
27035
27735
|
function docsRefreshContext(root, range) {
|
|
27036
|
-
const cwd =
|
|
27736
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
27037
27737
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27038
27738
|
return invalidRange(cwd);
|
|
27039
27739
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27055,7 +27755,7 @@ function docsRefreshContext(root, range) {
|
|
|
27055
27755
|
`) + `
|
|
27056
27756
|
`;
|
|
27057
27757
|
stdout += printSection("README Preview");
|
|
27058
|
-
const readme =
|
|
27758
|
+
const readme = path13.join(cwd, "README.md");
|
|
27059
27759
|
if (existsSync6(readme)) {
|
|
27060
27760
|
stdout += readTrimmed(readme, 220) + `
|
|
27061
27761
|
`;
|
|
@@ -27064,7 +27764,7 @@ function docsRefreshContext(root, range) {
|
|
|
27064
27764
|
`;
|
|
27065
27765
|
}
|
|
27066
27766
|
stdout += printSection("Package Scripts");
|
|
27067
|
-
const pkgPath =
|
|
27767
|
+
const pkgPath = path13.join(cwd, "package.json");
|
|
27068
27768
|
if (existsSync6(pkgPath)) {
|
|
27069
27769
|
try {
|
|
27070
27770
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
@@ -27078,7 +27778,7 @@ function docsRefreshContext(root, range) {
|
|
|
27078
27778
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27079
27779
|
}
|
|
27080
27780
|
function releaseNotesContext(root, rangeOrTag) {
|
|
27081
|
-
const cwd =
|
|
27781
|
+
const cwd = path13.resolve(repoRoot(root));
|
|
27082
27782
|
if (!rangeOrTag) {
|
|
27083
27783
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
27084
27784
|
`, exitCode: 1, cwd };
|
|
@@ -27116,7 +27816,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27116
27816
|
`;
|
|
27117
27817
|
stdout += printSection("Existing Release Files");
|
|
27118
27818
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
27119
|
-
if (existsSync6(
|
|
27819
|
+
if (existsSync6(path13.join(cwd, rel)))
|
|
27120
27820
|
stdout += rel + `
|
|
27121
27821
|
`;
|
|
27122
27822
|
}
|
|
@@ -27144,19 +27844,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27144
27844
|
}
|
|
27145
27845
|
|
|
27146
27846
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
27147
|
-
import
|
|
27148
|
-
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
27149
|
-
var trackerRequest = async (url, init) => {
|
|
27150
|
-
try {
|
|
27151
|
-
const res = await fetch(url, { method: init.method, headers: init.headers });
|
|
27152
|
-
const text = await res.text();
|
|
27153
|
-
if (!res.ok)
|
|
27154
|
-
return { status: res.status, stdout: "", stderr: text.slice(0, 200) };
|
|
27155
|
-
return { status: 0, stdout: text, stderr: "" };
|
|
27156
|
-
} catch (err) {
|
|
27157
|
-
return { status: 1, stdout: "", stderr: err instanceof Error ? err.message : "network error" };
|
|
27158
|
-
}
|
|
27159
|
-
};
|
|
27847
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
27160
27848
|
var repoPathFromRemote = (remote) => {
|
|
27161
27849
|
let rest = (remote || "").trim().replace(/\/+$/, "");
|
|
27162
27850
|
if (!rest)
|
|
@@ -27177,7 +27865,11 @@ var defaultCli = (bin, available) => {
|
|
|
27177
27865
|
if (!available)
|
|
27178
27866
|
return null;
|
|
27179
27867
|
return (args, root) => {
|
|
27180
|
-
const out =
|
|
27868
|
+
const out = spawnSync6(bin, args, {
|
|
27869
|
+
cwd: root,
|
|
27870
|
+
encoding: "utf8",
|
|
27871
|
+
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
27872
|
+
});
|
|
27181
27873
|
return Promise.resolve({
|
|
27182
27874
|
status: out.status ?? 1,
|
|
27183
27875
|
stdout: out.stdout ?? "",
|
|
@@ -27185,49 +27877,22 @@ var defaultCli = (bin, available) => {
|
|
|
27185
27877
|
});
|
|
27186
27878
|
};
|
|
27187
27879
|
};
|
|
27188
|
-
var tryCliTriple = async (cli, args, root, id, idKeys, bodyKeys) => {
|
|
27880
|
+
var tryCliTriple = async (cli, name, args, root, id, idKeys, bodyKeys) => {
|
|
27189
27881
|
if (!cli)
|
|
27190
|
-
return
|
|
27882
|
+
return { error: `${name} CLI is unavailable`, kind: "creds" };
|
|
27191
27883
|
const run = cli(args, root);
|
|
27192
27884
|
if (!run)
|
|
27193
|
-
return
|
|
27885
|
+
return { error: `${name} CLI is unavailable`, kind: "creds" };
|
|
27194
27886
|
const out = await run;
|
|
27195
27887
|
if (out.status !== 0)
|
|
27196
|
-
return
|
|
27888
|
+
return { error: `${name} CLI is not authenticated or the issue is unavailable`, kind: "creds" };
|
|
27197
27889
|
try {
|
|
27198
|
-
|
|
27890
|
+
const triple = parseTriple(JSON.parse(out.stdout), id, idKeys, bodyKeys);
|
|
27891
|
+
return triple ? { data: triple } : { error: `unexpected ${name} issue response`, kind: "request" };
|
|
27199
27892
|
} catch {
|
|
27200
|
-
return
|
|
27893
|
+
return { error: `invalid ${name} issue response`, kind: "request" };
|
|
27201
27894
|
}
|
|
27202
27895
|
};
|
|
27203
|
-
var originRemote = (root) => {
|
|
27204
|
-
const out = spawnSync5("git", ["remote", "get-url", "origin"], { cwd: root, encoding: "utf8" });
|
|
27205
|
-
return out.status === 0 ? (out.stdout ?? "").trim() || null : null;
|
|
27206
|
-
};
|
|
27207
|
-
var trackerCreds = (root, provider) => {
|
|
27208
|
-
const cfg = vcsConfig("load", root);
|
|
27209
|
-
if (!cfg || cfg.provider !== provider || typeof cfg.tokenPath !== "string" || !cfg.tokenPath)
|
|
27210
|
-
return { error: `unconfigured ${provider} provider` };
|
|
27211
|
-
let token = "";
|
|
27212
|
-
try {
|
|
27213
|
-
token = fs9.readFileSync(cfg.tokenPath, "utf8").trim();
|
|
27214
|
-
} catch {
|
|
27215
|
-
return { error: `missing ${provider} token` };
|
|
27216
|
-
}
|
|
27217
|
-
if (!token)
|
|
27218
|
-
return { error: `empty ${provider} token file` };
|
|
27219
|
-
if (provider === "gitlab") {
|
|
27220
|
-
const gitlab = cfg.gitlab;
|
|
27221
|
-
const apiUrl = String(gitlab?.apiUrl ?? "https://gitlab.com/api/v4").replace(/\/+$/, "");
|
|
27222
|
-
return { token, api: apiUrl };
|
|
27223
|
-
}
|
|
27224
|
-
const github = cfg.github;
|
|
27225
|
-
const host = String(github?.host ?? "github.com").toLowerCase();
|
|
27226
|
-
return {
|
|
27227
|
-
token,
|
|
27228
|
-
api: host === "github.com" ? "https://api.github.com" : `https://${host}/api/v3`
|
|
27229
|
-
};
|
|
27230
|
-
};
|
|
27231
27896
|
var parseTriple = (parsed, id, idKeys, bodyKeys) => {
|
|
27232
27897
|
const title = parsed.title;
|
|
27233
27898
|
if (typeof title !== "string")
|
|
@@ -27250,62 +27915,36 @@ var fetchGitHubIssueBody = async (ref, root, deps = {}) => {
|
|
|
27250
27915
|
const id = parseGhIssue(ref);
|
|
27251
27916
|
if (!/^\d+$/.test(id))
|
|
27252
27917
|
return { error: `invalid GitHub issue ref "${ref}"`, kind: "input" };
|
|
27253
|
-
const
|
|
27918
|
+
const remote = (deps.remote ?? pushRemote)(root);
|
|
27919
|
+
const repo = repoPathFromRemote(remote ?? "");
|
|
27254
27920
|
if (!repo)
|
|
27255
27921
|
return { error: "no origin remote to resolve the repository", kind: "input" };
|
|
27256
|
-
const
|
|
27257
|
-
|
|
27258
|
-
|
|
27259
|
-
|
|
27260
|
-
|
|
27261
|
-
|
|
27262
|
-
const out = await (deps.request ?? trackerRequest)(`${credentials.api}/repos/${repo}/issues/${id}`, {
|
|
27263
|
-
method: "GET",
|
|
27264
|
-
headers: {
|
|
27265
|
-
Authorization: `Bearer ${credentials.token}`,
|
|
27266
|
-
Accept: "application/vnd.github+json"
|
|
27267
|
-
}
|
|
27268
|
-
});
|
|
27269
|
-
if (out.status !== 0)
|
|
27270
|
-
return { error: out.stderr || "GitHub issue fetch failed", kind: "request" };
|
|
27271
|
-
try {
|
|
27272
|
-
const triple = parseTriple(JSON.parse(out.stdout), id, ["number"], ["body"]);
|
|
27273
|
-
if (!triple)
|
|
27274
|
-
return { error: "unexpected GitHub issue shape", kind: "request" };
|
|
27275
|
-
return { data: triple };
|
|
27276
|
-
} catch {
|
|
27277
|
-
return { error: "invalid GitHub issue response", kind: "request" };
|
|
27278
|
-
}
|
|
27922
|
+
const cfg = vcsConfig("load", root);
|
|
27923
|
+
const host = (deps.host ?? (cfg.ok && cfg.provider === "github" ? cfg.github?.host : null) ?? "github.com").toLowerCase();
|
|
27924
|
+
if (!remote || !hostingApiHostMatches(remote, host))
|
|
27925
|
+
return { error: "hosting API host does not match the push destination", kind: "input" };
|
|
27926
|
+
const repoArg = host === "github.com" ? repo : `${host}/${repo}`;
|
|
27927
|
+
return tryCliTriple(deps.cli ?? defaultCli("gh", hostingCliAvailable("github")), "gh", ["issue", "view", id, "--repo", repoArg, "--json", "number,title,body,state"], root, id, ["number"], ["body"]);
|
|
27279
27928
|
};
|
|
27280
27929
|
var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
27281
27930
|
const id = parseGhIssue(ref);
|
|
27282
27931
|
if (!/^\d+$/.test(id))
|
|
27283
27932
|
return { error: `invalid GitLab issue ref "${ref}"`, kind: "input" };
|
|
27284
|
-
const
|
|
27933
|
+
const remote = (deps.remote ?? pushRemote)(root);
|
|
27934
|
+
const repo = repoPathFromRemote(remote ?? "");
|
|
27285
27935
|
if (!repo)
|
|
27286
27936
|
return { error: "no origin remote to resolve the project", kind: "input" };
|
|
27287
|
-
const
|
|
27288
|
-
|
|
27289
|
-
|
|
27290
|
-
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
const out = await (deps.request ?? trackerRequest)(`${credentials.api}/projects/${encodeURIComponent(repo)}/issues/${id}`, { method: "GET", headers: { "PRIVATE-TOKEN": credentials.token } });
|
|
27294
|
-
if (out.status !== 0)
|
|
27295
|
-
return { error: out.stderr || "GitLab issue fetch failed", kind: "request" };
|
|
27296
|
-
try {
|
|
27297
|
-
const triple = parseTriple(JSON.parse(out.stdout), id, ["iid"], ["description"]);
|
|
27298
|
-
if (!triple)
|
|
27299
|
-
return { error: "unexpected GitLab issue shape", kind: "request" };
|
|
27300
|
-
return { data: triple };
|
|
27301
|
-
} catch {
|
|
27302
|
-
return { error: "invalid GitLab issue response", kind: "request" };
|
|
27303
|
-
}
|
|
27937
|
+
const cfg = vcsConfig("load", root);
|
|
27938
|
+
const host = (deps.host ?? (cfg.ok && cfg.provider === "gitlab" ? cfg.gitlab?.host : null) ?? "gitlab.com").toLowerCase();
|
|
27939
|
+
if (!remote || !hostingApiHostMatches(remote, host))
|
|
27940
|
+
return { error: "hosting API host does not match the push destination", kind: "input" };
|
|
27941
|
+
const repoArg = host === "gitlab.com" ? repo : `https://${host}/${repo}`;
|
|
27942
|
+
return tryCliTriple(deps.cli ?? defaultCli("glab", hostingCliAvailable("gitlab")), "glab", ["issue", "view", id, "-R", repoArg, "-F", "json"], root, id, ["iid"], ["description"]);
|
|
27304
27943
|
};
|
|
27305
27944
|
|
|
27306
27945
|
// packages/workit-core/src/core/youtrack.ts
|
|
27307
|
-
import
|
|
27308
|
-
import
|
|
27946
|
+
import fs9 from "node:fs";
|
|
27947
|
+
import path14 from "node:path";
|
|
27309
27948
|
|
|
27310
27949
|
// packages/workit-core/src/core/templates.ts
|
|
27311
27950
|
var repoRoot2 = assetRoot();
|
|
@@ -27317,23 +27956,23 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
27317
27956
|
|
|
27318
27957
|
// packages/workit-core/src/core/youtrack.ts
|
|
27319
27958
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
27320
|
-
var
|
|
27321
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27322
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27959
|
+
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
27960
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(configDir(), "youtrack.json");
|
|
27961
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(resolveConfigDir(), "youtrack.json");
|
|
27323
27962
|
var youTrackTokenModeOk = (p) => {
|
|
27324
27963
|
if (process.platform === "win32")
|
|
27325
27964
|
return true;
|
|
27326
|
-
const mode =
|
|
27965
|
+
const mode = fs9.statSync(p).mode & 511;
|
|
27327
27966
|
return mode === 384;
|
|
27328
27967
|
};
|
|
27329
27968
|
function readYouTrackConfig(required) {
|
|
27330
27969
|
const cfgPath = youTrackConfigPath();
|
|
27331
|
-
if (!
|
|
27970
|
+
if (!fs9.existsSync(cfgPath)) {
|
|
27332
27971
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
27333
27972
|
}
|
|
27334
27973
|
let parsed;
|
|
27335
27974
|
try {
|
|
27336
|
-
parsed = JSON.parse(
|
|
27975
|
+
parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
|
|
27337
27976
|
} catch {
|
|
27338
27977
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
27339
27978
|
}
|
|
@@ -27344,11 +27983,11 @@ function readYouTrackConfig(required) {
|
|
|
27344
27983
|
}
|
|
27345
27984
|
var readYouTrackContextConfig = () => {
|
|
27346
27985
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
27347
|
-
if (!
|
|
27986
|
+
if (!fs9.existsSync(cfgPath))
|
|
27348
27987
|
return { data: {} };
|
|
27349
27988
|
let parsed;
|
|
27350
27989
|
try {
|
|
27351
|
-
parsed = JSON.parse(
|
|
27990
|
+
parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
|
|
27352
27991
|
} catch {
|
|
27353
27992
|
return { error: "YouTrack configuration is unavailable" };
|
|
27354
27993
|
}
|
|
@@ -27373,12 +28012,12 @@ function youTrackGreeting(configOverride) {
|
|
|
27373
28012
|
const cfgPath = configOverride ?? youTrackConfigPath();
|
|
27374
28013
|
let parsed;
|
|
27375
28014
|
try {
|
|
27376
|
-
parsed = JSON.parse(
|
|
28015
|
+
parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
|
|
27377
28016
|
} catch {
|
|
27378
28017
|
return {
|
|
27379
28018
|
stdout: "",
|
|
27380
28019
|
exitCode: 1,
|
|
27381
|
-
stderr:
|
|
28020
|
+
stderr: fs9.existsSync(cfgPath) ? `${cfgPath} is not valid JSON` : `missing youtrack.json: ${cfgPath}`
|
|
27382
28021
|
};
|
|
27383
28022
|
}
|
|
27384
28023
|
if (!isConfigObject(parsed)) {
|
|
@@ -27424,15 +28063,15 @@ var youTrackToken = () => {
|
|
|
27424
28063
|
if ("error" in loaded)
|
|
27425
28064
|
return loaded;
|
|
27426
28065
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
27427
|
-
const tokenPath = tokenFile ?
|
|
27428
|
-
if (!tokenPath || !
|
|
28066
|
+
const tokenPath = tokenFile ? path14.isAbsolute(tokenFile) ? path14.resolve(tokenFile) : path14.resolve(process.cwd(), tokenFile) : "";
|
|
28067
|
+
if (!tokenPath || !fs9.existsSync(tokenPath))
|
|
27429
28068
|
return { error: "missing youtrack.token" };
|
|
27430
28069
|
if (!youTrackTokenModeOk(tokenPath))
|
|
27431
28070
|
return { error: "youtrack.token mode must be 0600" };
|
|
27432
|
-
const token =
|
|
28071
|
+
const token = fs9.readFileSync(tokenPath, "utf8").trim();
|
|
27433
28072
|
if (!token)
|
|
27434
28073
|
return { error: "empty token file" };
|
|
27435
|
-
if (token ===
|
|
28074
|
+
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
27436
28075
|
return {
|
|
27437
28076
|
error: "token file still has placeholder YOUR_TOKEN_HERE — edit the file locally, then run /wk-status"
|
|
27438
28077
|
};
|
|
@@ -27495,9 +28134,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
27495
28134
|
} catch {
|
|
27496
28135
|
continue;
|
|
27497
28136
|
}
|
|
27498
|
-
if (!
|
|
28137
|
+
if (!fs9.existsSync(full))
|
|
27499
28138
|
continue;
|
|
27500
|
-
const text =
|
|
28139
|
+
const text = fs9.readFileSync(full, "utf8");
|
|
27501
28140
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
27502
28141
|
if (m)
|
|
27503
28142
|
return m[1];
|
|
@@ -27731,6 +28370,7 @@ var readExternalContext = async (root, payload) => {
|
|
|
27731
28370
|
}
|
|
27732
28371
|
}
|
|
27733
28372
|
};
|
|
28373
|
+
var targetWriterActionLease = Symbol("target-writer-action-lease");
|
|
27734
28374
|
|
|
27735
28375
|
// packages/workit-mcp/src/server.ts
|
|
27736
28376
|
class McpCapabilityUnavailableError extends Error {
|
|
@@ -27775,13 +28415,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
27775
28415
|
if (!workspaceRoot)
|
|
27776
28416
|
return [];
|
|
27777
28417
|
const roots = new Set;
|
|
27778
|
-
for (const candidate of [workspaceRoot,
|
|
27779
|
-
if (
|
|
28418
|
+
for (const candidate of [workspaceRoot, path15.resolve(workspaceRoot)]) {
|
|
28419
|
+
if (path15.isAbsolute(candidate) && candidate !== path15.parse(candidate).root)
|
|
27780
28420
|
roots.add(candidate);
|
|
27781
28421
|
}
|
|
27782
28422
|
try {
|
|
27783
|
-
const canonical =
|
|
27784
|
-
if (canonical !==
|
|
28423
|
+
const canonical = realpathSync8(workspaceRoot);
|
|
28424
|
+
if (canonical !== path15.parse(canonical).root)
|
|
27785
28425
|
roots.add(canonical);
|
|
27786
28426
|
} catch {}
|
|
27787
28427
|
return [...roots].sort((left, right) => right.length - left.length);
|
|
@@ -27882,11 +28522,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
27882
28522
|
};
|
|
27883
28523
|
function createMcpServer(host, contextProvider) {
|
|
27884
28524
|
assertMcpHost(host);
|
|
27885
|
-
const here =
|
|
28525
|
+
const here = path15.dirname(fileURLToPath2(import.meta.url));
|
|
27886
28526
|
const sourceMarker = markSourcesLoaded([
|
|
27887
|
-
|
|
27888
|
-
|
|
27889
|
-
|
|
28527
|
+
path15.join(here, "server.ts"),
|
|
28528
|
+
path15.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
28529
|
+
path15.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
27890
28530
|
].filter((file) => existsSync7(file)));
|
|
27891
28531
|
let staleWarned = false;
|
|
27892
28532
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -28066,8 +28706,8 @@ var main = async () => {
|
|
|
28066
28706
|
process.exitCode = 2;
|
|
28067
28707
|
}
|
|
28068
28708
|
};
|
|
28069
|
-
var entryPath = process.argv[1] ?
|
|
28070
|
-
if (entryPath ===
|
|
28709
|
+
var entryPath = process.argv[1] ? path16.resolve(process.argv[1]) : "";
|
|
28710
|
+
if (entryPath === path16.resolve(fileURLToPath3(import.meta.url)))
|
|
28071
28711
|
await main();
|
|
28072
28712
|
export {
|
|
28073
28713
|
McpCapabilityUnavailableError,
|