@drisp/cli 0.5.4 → 0.5.7
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/athena-gateway.js +29 -12
- package/dist/{chunk-QVXHUJPH.js → chunk-UKMVFGY2.js} +618 -56
- package/dist/cli.js +14 -4
- package/dist/dashboard-daemon.js +1 -1
- package/package.json +1 -1
package/dist/athena-gateway.js
CHANGED
|
@@ -2241,7 +2241,7 @@ var cachedVersion = null;
|
|
|
2241
2241
|
function readVersion() {
|
|
2242
2242
|
if (cachedVersion !== null) return cachedVersion;
|
|
2243
2243
|
try {
|
|
2244
|
-
const injected = "0.5.
|
|
2244
|
+
const injected = "0.5.7";
|
|
2245
2245
|
if (typeof injected === "string" && injected.length > 0) {
|
|
2246
2246
|
cachedVersion = injected;
|
|
2247
2247
|
return cachedVersion;
|
|
@@ -2876,17 +2876,18 @@ var InboundQueue = class {
|
|
|
2876
2876
|
const row = this.db.prepare("SELECT COUNT(*) as n FROM inbound_queue").get();
|
|
2877
2877
|
return row.n;
|
|
2878
2878
|
}
|
|
2879
|
-
enqueue(inbound) {
|
|
2879
|
+
enqueue(inbound, key) {
|
|
2880
2880
|
if (this.size() >= this.maxEntries) {
|
|
2881
2881
|
return { kind: "rejected", reason: "queue_full" };
|
|
2882
2882
|
}
|
|
2883
2883
|
const stmt = this.db.prepare(
|
|
2884
2884
|
`INSERT INTO inbound_queue
|
|
2885
|
-
(channel_id, account_id, idempotency_key, payload_json, created_at)
|
|
2886
|
-
VALUES (?, ?, ?, ?, ?)
|
|
2885
|
+
(attachment_id, channel_id, account_id, idempotency_key, payload_json, created_at)
|
|
2886
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
2887
2887
|
ON CONFLICT(channel_id, account_id, idempotency_key) DO NOTHING`
|
|
2888
2888
|
);
|
|
2889
2889
|
const result = stmt.run(
|
|
2890
|
+
key ?? null,
|
|
2890
2891
|
inbound.location.channelId,
|
|
2891
2892
|
inbound.location.accountId,
|
|
2892
2893
|
inbound.idempotencyKey,
|
|
@@ -2898,12 +2899,17 @@ var InboundQueue = class {
|
|
|
2898
2899
|
}
|
|
2899
2900
|
return { kind: "queued", id: Number(result.lastInsertRowid) };
|
|
2900
2901
|
}
|
|
2901
|
-
/** Atomically read and remove
|
|
2902
|
-
drain() {
|
|
2902
|
+
/** Atomically read and remove parked entries for one attachment slot. */
|
|
2903
|
+
drain(key) {
|
|
2903
2904
|
return this.db.transaction(() => {
|
|
2904
|
-
const rows = this.db.prepare(
|
|
2905
|
+
const rows = this.db.prepare(
|
|
2906
|
+
`SELECT id, payload_json
|
|
2907
|
+
FROM inbound_queue
|
|
2908
|
+
WHERE attachment_id IS ?
|
|
2909
|
+
ORDER BY id ASC`
|
|
2910
|
+
).all(key ?? null);
|
|
2905
2911
|
if (rows.length > 0) {
|
|
2906
|
-
this.db.prepare("DELETE FROM inbound_queue").run();
|
|
2912
|
+
this.db.prepare("DELETE FROM inbound_queue WHERE attachment_id IS ?").run(key ?? null);
|
|
2907
2913
|
}
|
|
2908
2914
|
return rows.map((r) => ({
|
|
2909
2915
|
id: r.id,
|
|
@@ -3022,7 +3028,7 @@ var DispatchPipeline = class {
|
|
|
3022
3028
|
const key = options.attachmentId;
|
|
3023
3029
|
const current = this.bindingStore.getCurrentByAttachment(key);
|
|
3024
3030
|
if (!current || !this.bindingStore.hasActiveBindingForAttachment(key)) {
|
|
3025
|
-
const result = this.inboundQueue.enqueue(inbound);
|
|
3031
|
+
const result = this.inboundQueue.enqueue(inbound, key);
|
|
3026
3032
|
if (result.kind === "queued") {
|
|
3027
3033
|
this.log?.(
|
|
3028
3034
|
"info",
|
|
@@ -3207,9 +3213,9 @@ var DispatchPipeline = class {
|
|
|
3207
3213
|
}
|
|
3208
3214
|
drainPending(key) {
|
|
3209
3215
|
const current = this.bindingStore.getCurrentByAttachment(key);
|
|
3210
|
-
if (!current || !this.bindingStore.
|
|
3216
|
+
if (!current || !this.bindingStore.hasActiveBindingForAttachment(key))
|
|
3211
3217
|
return;
|
|
3212
|
-
const parked = this.inboundQueue.drain();
|
|
3218
|
+
const parked = this.inboundQueue.drain(key);
|
|
3213
3219
|
let dispatched = 0;
|
|
3214
3220
|
let dropped = 0;
|
|
3215
3221
|
for (const { inbound } of parked) {
|
|
@@ -3548,7 +3554,7 @@ function questionFingerprint(req) {
|
|
|
3548
3554
|
import fs3 from "fs";
|
|
3549
3555
|
import path3 from "path";
|
|
3550
3556
|
import Database from "better-sqlite3";
|
|
3551
|
-
var GATEWAY_STATE_VERSION =
|
|
3557
|
+
var GATEWAY_STATE_VERSION = 2;
|
|
3552
3558
|
function openGatewayState(dbPath) {
|
|
3553
3559
|
if (dbPath !== ":memory:") {
|
|
3554
3560
|
fs3.mkdirSync(path3.dirname(dbPath), { recursive: true, mode: 448 });
|
|
@@ -3576,6 +3582,7 @@ function initGatewayStateSchema(db) {
|
|
|
3576
3582
|
-- same provider message from being parked twice if an adapter retries.
|
|
3577
3583
|
CREATE TABLE IF NOT EXISTS inbound_queue (
|
|
3578
3584
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
3585
|
+
attachment_id TEXT,
|
|
3579
3586
|
channel_id TEXT NOT NULL,
|
|
3580
3587
|
account_id TEXT NOT NULL,
|
|
3581
3588
|
idempotency_key TEXT NOT NULL,
|
|
@@ -3611,6 +3618,16 @@ function initGatewayStateSchema(db) {
|
|
|
3611
3618
|
db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(
|
|
3612
3619
|
GATEWAY_STATE_VERSION
|
|
3613
3620
|
);
|
|
3621
|
+
return;
|
|
3622
|
+
}
|
|
3623
|
+
const inboundColumns = db.prepare("PRAGMA table_info(inbound_queue)").all();
|
|
3624
|
+
if (!inboundColumns.some((column) => column.name === "attachment_id")) {
|
|
3625
|
+
db.prepare("ALTER TABLE inbound_queue ADD COLUMN attachment_id TEXT").run();
|
|
3626
|
+
}
|
|
3627
|
+
if (existing.version < GATEWAY_STATE_VERSION) {
|
|
3628
|
+
db.prepare("UPDATE schema_version SET version = ?").run(
|
|
3629
|
+
GATEWAY_STATE_VERSION
|
|
3630
|
+
);
|
|
3614
3631
|
}
|
|
3615
3632
|
}
|
|
3616
3633
|
|
|
@@ -7700,6 +7700,8 @@ function generateNeutralTitle(event, g) {
|
|
|
7700
7700
|
return truncate(
|
|
7701
7701
|
`Cloud fn \u2717 ${event.data.function_name}: ${event.data.error_message}`
|
|
7702
7702
|
);
|
|
7703
|
+
case "artifacts.manifest":
|
|
7704
|
+
return "Artifacts manifest";
|
|
7703
7705
|
}
|
|
7704
7706
|
}
|
|
7705
7707
|
|
|
@@ -11665,6 +11667,7 @@ async function runExec(options) {
|
|
|
11665
11667
|
let mappedFinalMessage = null;
|
|
11666
11668
|
let adapterSessionId = null;
|
|
11667
11669
|
let activeRunId = null;
|
|
11670
|
+
let beforeTerminalCompletionRan = false;
|
|
11668
11671
|
let store;
|
|
11669
11672
|
try {
|
|
11670
11673
|
store = sessionStoreFactory({
|
|
@@ -11787,6 +11790,57 @@ async function runExec(options) {
|
|
|
11787
11790
|
feedEvents
|
|
11788
11791
|
});
|
|
11789
11792
|
}
|
|
11793
|
+
const runBeforeTerminalCompletion = async () => {
|
|
11794
|
+
if (beforeTerminalCompletionRan || latch.hasFailure() || !options.beforeTerminalCompletion) {
|
|
11795
|
+
return;
|
|
11796
|
+
}
|
|
11797
|
+
beforeTerminalCompletionRan = true;
|
|
11798
|
+
const resolved = resolveFinalMessage({
|
|
11799
|
+
streamMessage: streamFinalMessage,
|
|
11800
|
+
mappedMessage: mappedFinalMessage
|
|
11801
|
+
});
|
|
11802
|
+
const provisionalResult = {
|
|
11803
|
+
success: true,
|
|
11804
|
+
exitCode: EXEC_EXIT_CODE.SUCCESS,
|
|
11805
|
+
athenaSessionId: options.ephemeral ? null : athenaSessionId,
|
|
11806
|
+
adapterSessionId,
|
|
11807
|
+
finalMessage: resolved.message,
|
|
11808
|
+
tokens: cumulativeTokens,
|
|
11809
|
+
durationMs: Math.max(0, now() - startTs)
|
|
11810
|
+
};
|
|
11811
|
+
try {
|
|
11812
|
+
const feedEvents = await options.beforeTerminalCompletion({
|
|
11813
|
+
result: provisionalResult,
|
|
11814
|
+
runId: activeRunId
|
|
11815
|
+
});
|
|
11816
|
+
if (feedEvents && feedEvents.length > 0) {
|
|
11817
|
+
publishFeedEvents(feedEvents);
|
|
11818
|
+
}
|
|
11819
|
+
} catch (error) {
|
|
11820
|
+
latch.register({
|
|
11821
|
+
kind: "output",
|
|
11822
|
+
message: `Artifact upload failed: ${error instanceof Error ? error.message : String(error)}`
|
|
11823
|
+
});
|
|
11824
|
+
}
|
|
11825
|
+
};
|
|
11826
|
+
const writeLastMessageBeforeTerminalCompletion = async () => {
|
|
11827
|
+
if (latch.hasFailure() || !options.outputLastMessagePath) return;
|
|
11828
|
+
const resolved = resolveFinalMessage({
|
|
11829
|
+
streamMessage: streamFinalMessage,
|
|
11830
|
+
mappedMessage: mappedFinalMessage
|
|
11831
|
+
});
|
|
11832
|
+
try {
|
|
11833
|
+
await output.writeLastMessage(
|
|
11834
|
+
options.outputLastMessagePath,
|
|
11835
|
+
resolved.message
|
|
11836
|
+
);
|
|
11837
|
+
} catch (error) {
|
|
11838
|
+
latch.register({
|
|
11839
|
+
kind: "output",
|
|
11840
|
+
message: `Failed writing --output-last-message: ${error instanceof Error ? error.message : String(error)}`
|
|
11841
|
+
});
|
|
11842
|
+
}
|
|
11843
|
+
};
|
|
11790
11844
|
const unsubscribeEvent = runtime.onEvent((runtimeEvent) => {
|
|
11791
11845
|
adapterSessionId = runtimeEvent.sessionId;
|
|
11792
11846
|
if (runtimeEvent.sessionId && activeRunId && !linkedAdapterSessions.has(runtimeEvent.sessionId)) {
|
|
@@ -11956,6 +12010,8 @@ async function runExec(options) {
|
|
|
11956
12010
|
if (dashboardDecisionTimer) {
|
|
11957
12011
|
clearInterval(dashboardDecisionTimer);
|
|
11958
12012
|
}
|
|
12013
|
+
await writeLastMessageBeforeTerminalCompletion();
|
|
12014
|
+
await runBeforeTerminalCompletion();
|
|
11959
12015
|
await sessionController.kill();
|
|
11960
12016
|
unsubscribeEvent();
|
|
11961
12017
|
unsubscribeDecision();
|
|
@@ -11974,19 +12030,6 @@ async function runExec(options) {
|
|
|
11974
12030
|
output.warn(warning);
|
|
11975
12031
|
output.emitJsonEvent("exec.warning", { message: warning });
|
|
11976
12032
|
}
|
|
11977
|
-
if (!latch.hasFailure() && options.outputLastMessagePath) {
|
|
11978
|
-
try {
|
|
11979
|
-
await output.writeLastMessage(
|
|
11980
|
-
options.outputLastMessagePath,
|
|
11981
|
-
resolvedFinalMessage.message
|
|
11982
|
-
);
|
|
11983
|
-
} catch (error) {
|
|
11984
|
-
latch.register({
|
|
11985
|
-
kind: "output",
|
|
11986
|
-
message: `Failed writing --output-last-message: ${error instanceof Error ? error.message : String(error)}`
|
|
11987
|
-
});
|
|
11988
|
-
}
|
|
11989
|
-
}
|
|
11990
12033
|
const failure = latch.current();
|
|
11991
12034
|
const exitCode = exitCodeFromFailure(failure);
|
|
11992
12035
|
const success = exitCode === EXEC_EXIT_CODE.SUCCESS;
|
|
@@ -12586,6 +12629,370 @@ async function createRemoteRunEventPublisher({
|
|
|
12586
12629
|
};
|
|
12587
12630
|
}
|
|
12588
12631
|
|
|
12632
|
+
// src/app/dashboard/artifactCapture.ts
|
|
12633
|
+
import { execFile } from "child_process";
|
|
12634
|
+
import crypto3 from "crypto";
|
|
12635
|
+
import fs21 from "fs/promises";
|
|
12636
|
+
import path19 from "path";
|
|
12637
|
+
import { promisify } from "util";
|
|
12638
|
+
var execFileAsync = promisify(execFile);
|
|
12639
|
+
function parseArtifactUploadSpec(value) {
|
|
12640
|
+
if (typeof value !== "object" || value === null) return null;
|
|
12641
|
+
const obj = value;
|
|
12642
|
+
const hasArtifactUpload = Object.hasOwn(obj, "artifactUpload");
|
|
12643
|
+
const hasArtifacts = Object.hasOwn(obj, "artifacts");
|
|
12644
|
+
if (!hasArtifactUpload && !hasArtifacts) return null;
|
|
12645
|
+
const candidate = hasArtifactUpload ? obj["artifactUpload"] : obj["artifacts"];
|
|
12646
|
+
if (typeof candidate !== "object" || candidate === null) {
|
|
12647
|
+
throw new Error("artifact upload spec must be an object");
|
|
12648
|
+
}
|
|
12649
|
+
const spec = candidate;
|
|
12650
|
+
const bucket = spec["bucket"];
|
|
12651
|
+
const prefix = spec["prefix"];
|
|
12652
|
+
const normalizedPrefix = typeof prefix === "string" ? prefix.replace(/^\/+|\/+$/g, "") : null;
|
|
12653
|
+
const accessToken = spec["accessToken"] ?? (typeof spec["credentials"] === "object" && spec["credentials"] !== null ? spec["credentials"]["accessToken"] : void 0);
|
|
12654
|
+
if (typeof bucket !== "string" || bucket.length === 0 || !normalizedPrefix || typeof accessToken !== "string" || accessToken.length === 0) {
|
|
12655
|
+
throw new Error(
|
|
12656
|
+
"artifact upload spec must include bucket, prefix, and accessToken"
|
|
12657
|
+
);
|
|
12658
|
+
}
|
|
12659
|
+
return {
|
|
12660
|
+
bucket,
|
|
12661
|
+
prefix: normalizedPrefix,
|
|
12662
|
+
accessToken,
|
|
12663
|
+
includeIgnored: stringArray(spec["includeIgnored"]),
|
|
12664
|
+
hardDeny: [...DEFAULT_HARD_DENY, ...stringArray(spec["hardDeny"])]
|
|
12665
|
+
};
|
|
12666
|
+
}
|
|
12667
|
+
async function captureAndUploadArtifacts(input) {
|
|
12668
|
+
const now = input.now ?? Date.now;
|
|
12669
|
+
const uploadObject = input.uploadObject ?? uploadGcsObject;
|
|
12670
|
+
const payloads = await collectArtifactPayloads({
|
|
12671
|
+
projectDir: input.projectDir,
|
|
12672
|
+
includeIgnored: input.spec.includeIgnored,
|
|
12673
|
+
hardDeny: input.spec.hardDeny
|
|
12674
|
+
});
|
|
12675
|
+
const entries = [];
|
|
12676
|
+
for (const [idx, payload] of payloads.entries()) {
|
|
12677
|
+
const id = `${String(idx + 1).padStart(4, "0")}-${safeObjectSegment(
|
|
12678
|
+
payload.kind
|
|
12679
|
+
)}`;
|
|
12680
|
+
const object = joinObjectName(
|
|
12681
|
+
input.spec.prefix,
|
|
12682
|
+
"payloads",
|
|
12683
|
+
`${id}-${safeObjectSegment(payload.path)}`
|
|
12684
|
+
);
|
|
12685
|
+
await uploadObject({
|
|
12686
|
+
bucket: input.spec.bucket,
|
|
12687
|
+
objectName: object,
|
|
12688
|
+
body: payload.bytes,
|
|
12689
|
+
contentType: contentTypeFor(payload.path),
|
|
12690
|
+
accessToken: input.spec.accessToken
|
|
12691
|
+
});
|
|
12692
|
+
entries.push({
|
|
12693
|
+
id,
|
|
12694
|
+
kind: payload.kind,
|
|
12695
|
+
path: payload.path,
|
|
12696
|
+
object,
|
|
12697
|
+
size: payload.bytes.byteLength,
|
|
12698
|
+
sha256: sha256(payload.bytes)
|
|
12699
|
+
});
|
|
12700
|
+
}
|
|
12701
|
+
const manifestObject = joinObjectName(input.spec.prefix, "manifest.json");
|
|
12702
|
+
const manifest = {
|
|
12703
|
+
version: 1,
|
|
12704
|
+
runId: input.runId,
|
|
12705
|
+
athenaSessionId: input.result.athenaSessionId,
|
|
12706
|
+
adapterSessionId: input.result.adapterSessionId,
|
|
12707
|
+
createdAt: new Date(now()).toISOString(),
|
|
12708
|
+
entries,
|
|
12709
|
+
objects: {
|
|
12710
|
+
bucket: input.spec.bucket,
|
|
12711
|
+
prefix: input.spec.prefix,
|
|
12712
|
+
manifest: manifestObject
|
|
12713
|
+
}
|
|
12714
|
+
};
|
|
12715
|
+
await uploadObject({
|
|
12716
|
+
bucket: input.spec.bucket,
|
|
12717
|
+
objectName: manifestObject,
|
|
12718
|
+
body: Buffer.from(`${JSON.stringify(manifest, null, 2)}
|
|
12719
|
+
`),
|
|
12720
|
+
contentType: "application/json",
|
|
12721
|
+
accessToken: input.spec.accessToken
|
|
12722
|
+
});
|
|
12723
|
+
return {
|
|
12724
|
+
manifest,
|
|
12725
|
+
feedEvent: makeArtifactManifestFeedEvent({
|
|
12726
|
+
manifest,
|
|
12727
|
+
result: input.result,
|
|
12728
|
+
runId: input.runId,
|
|
12729
|
+
ts: now()
|
|
12730
|
+
})
|
|
12731
|
+
};
|
|
12732
|
+
}
|
|
12733
|
+
async function collectArtifactPayloads(input) {
|
|
12734
|
+
const hardDeny = input.hardDeny ?? DEFAULT_HARD_DENY;
|
|
12735
|
+
const payloads = [];
|
|
12736
|
+
if (!await isGitWorkspace(input.projectDir)) {
|
|
12737
|
+
return payloads;
|
|
12738
|
+
}
|
|
12739
|
+
const trackedDiff = await gitDiffForAllowedPaths({
|
|
12740
|
+
projectDir: input.projectDir,
|
|
12741
|
+
nameArgs: ["diff", "--name-only", "-z", "--"],
|
|
12742
|
+
diffArgs: ["diff", "--binary", "--"],
|
|
12743
|
+
hardDeny
|
|
12744
|
+
});
|
|
12745
|
+
if (trackedDiff.length > 0) {
|
|
12746
|
+
payloads.push({
|
|
12747
|
+
kind: "tracked_diff",
|
|
12748
|
+
path: "git/tracked.diff",
|
|
12749
|
+
bytes: Buffer.from(trackedDiff)
|
|
12750
|
+
});
|
|
12751
|
+
}
|
|
12752
|
+
const stagedDiff = await gitDiffForAllowedPaths({
|
|
12753
|
+
projectDir: input.projectDir,
|
|
12754
|
+
nameArgs: ["diff", "--name-only", "-z", "--cached", "--"],
|
|
12755
|
+
diffArgs: ["diff", "--binary", "--cached", "--"],
|
|
12756
|
+
hardDeny
|
|
12757
|
+
});
|
|
12758
|
+
if (stagedDiff.length > 0) {
|
|
12759
|
+
payloads.push({
|
|
12760
|
+
kind: "staged_diff",
|
|
12761
|
+
path: "git/staged.diff",
|
|
12762
|
+
bytes: Buffer.from(stagedDiff)
|
|
12763
|
+
});
|
|
12764
|
+
}
|
|
12765
|
+
const upstream = await gitMaybe(input.projectDir, [
|
|
12766
|
+
"rev-parse",
|
|
12767
|
+
"--abbrev-ref",
|
|
12768
|
+
"--symbolic-full-name",
|
|
12769
|
+
"@{u}"
|
|
12770
|
+
]);
|
|
12771
|
+
if (upstream.trim().length > 0) {
|
|
12772
|
+
const range = `${upstream.trim()}..HEAD`;
|
|
12773
|
+
const commits = await gitDiffForAllowedPaths({
|
|
12774
|
+
projectDir: input.projectDir,
|
|
12775
|
+
nameArgs: ["diff", "--name-only", "-z", range, "--"],
|
|
12776
|
+
diffArgs: ["format-patch", "--stdout", range, "--"],
|
|
12777
|
+
hardDeny
|
|
12778
|
+
});
|
|
12779
|
+
if (commits.length > 0) {
|
|
12780
|
+
payloads.push({
|
|
12781
|
+
kind: "unpushed_commits",
|
|
12782
|
+
path: "git/unpushed.patch",
|
|
12783
|
+
bytes: Buffer.from(commits)
|
|
12784
|
+
});
|
|
12785
|
+
}
|
|
12786
|
+
}
|
|
12787
|
+
const untracked = splitNul(
|
|
12788
|
+
await git(input.projectDir, [
|
|
12789
|
+
"ls-files",
|
|
12790
|
+
"--others",
|
|
12791
|
+
"--exclude-standard",
|
|
12792
|
+
"-z"
|
|
12793
|
+
])
|
|
12794
|
+
);
|
|
12795
|
+
for (const rel of untracked) {
|
|
12796
|
+
if (!isAllowedRelativePath(rel, hardDeny)) continue;
|
|
12797
|
+
const bytes = await readWorkspaceFile(input.projectDir, rel);
|
|
12798
|
+
if (!bytes) continue;
|
|
12799
|
+
payloads.push({
|
|
12800
|
+
kind: "untracked_file",
|
|
12801
|
+
path: rel,
|
|
12802
|
+
bytes
|
|
12803
|
+
});
|
|
12804
|
+
}
|
|
12805
|
+
for (const rel of input.includeIgnored ?? []) {
|
|
12806
|
+
if (!isAllowedRelativePath(rel, hardDeny)) continue;
|
|
12807
|
+
if (!await isIgnored(input.projectDir, rel)) continue;
|
|
12808
|
+
const bytes = await readWorkspaceFile(input.projectDir, rel);
|
|
12809
|
+
if (!bytes) continue;
|
|
12810
|
+
payloads.push({
|
|
12811
|
+
kind: "included_ignored_file",
|
|
12812
|
+
path: rel,
|
|
12813
|
+
bytes
|
|
12814
|
+
});
|
|
12815
|
+
}
|
|
12816
|
+
return payloads;
|
|
12817
|
+
}
|
|
12818
|
+
async function readWorkspaceFile(projectDir, rel) {
|
|
12819
|
+
const absolute = path19.resolve(projectDir, rel);
|
|
12820
|
+
const workspaceRoot = await fs21.realpath(projectDir);
|
|
12821
|
+
let stat;
|
|
12822
|
+
try {
|
|
12823
|
+
stat = await fs21.lstat(absolute);
|
|
12824
|
+
} catch {
|
|
12825
|
+
return null;
|
|
12826
|
+
}
|
|
12827
|
+
if (!stat.isFile() || stat.isSymbolicLink()) return null;
|
|
12828
|
+
const real = await fs21.realpath(absolute);
|
|
12829
|
+
const relativeToRoot = path19.relative(workspaceRoot, real);
|
|
12830
|
+
if (relativeToRoot === ".." || relativeToRoot.startsWith(`..${path19.sep}`) || path19.isAbsolute(relativeToRoot)) {
|
|
12831
|
+
return null;
|
|
12832
|
+
}
|
|
12833
|
+
return fs21.readFile(absolute);
|
|
12834
|
+
}
|
|
12835
|
+
async function gitDiffForAllowedPaths(input) {
|
|
12836
|
+
const paths = splitNul(await git(input.projectDir, input.nameArgs)).filter(
|
|
12837
|
+
(rel) => isAllowedRelativePath(rel, input.hardDeny)
|
|
12838
|
+
);
|
|
12839
|
+
if (paths.length === 0) return "";
|
|
12840
|
+
return git(input.projectDir, [...input.diffArgs, ...paths]);
|
|
12841
|
+
}
|
|
12842
|
+
async function uploadGcsObject(input) {
|
|
12843
|
+
const url = new URL(
|
|
12844
|
+
`https://storage.googleapis.com/upload/storage/v1/b/${encodeURIComponent(
|
|
12845
|
+
input.bucket
|
|
12846
|
+
)}/o`
|
|
12847
|
+
);
|
|
12848
|
+
url.searchParams.set("uploadType", "media");
|
|
12849
|
+
url.searchParams.set("name", input.objectName);
|
|
12850
|
+
const response = await fetch(url, {
|
|
12851
|
+
method: "POST",
|
|
12852
|
+
headers: {
|
|
12853
|
+
authorization: `Bearer ${input.accessToken}`,
|
|
12854
|
+
"content-type": input.contentType
|
|
12855
|
+
},
|
|
12856
|
+
body: input.body.buffer.slice(
|
|
12857
|
+
input.body.byteOffset,
|
|
12858
|
+
input.body.byteOffset + input.body.byteLength
|
|
12859
|
+
)
|
|
12860
|
+
});
|
|
12861
|
+
if (!response.ok) {
|
|
12862
|
+
throw new Error(
|
|
12863
|
+
`GCS upload ${input.objectName} failed with HTTP ${response.status}`
|
|
12864
|
+
);
|
|
12865
|
+
}
|
|
12866
|
+
}
|
|
12867
|
+
function makeArtifactManifestFeedEvent(input) {
|
|
12868
|
+
return {
|
|
12869
|
+
event_id: `${input.runId}:artifacts-manifest`,
|
|
12870
|
+
seq: 0,
|
|
12871
|
+
ts: input.ts,
|
|
12872
|
+
session_id: input.result.athenaSessionId ?? input.runId,
|
|
12873
|
+
run_id: input.runId,
|
|
12874
|
+
kind: "artifacts.manifest",
|
|
12875
|
+
level: "info",
|
|
12876
|
+
actor_id: "system",
|
|
12877
|
+
title: "Artifacts manifest",
|
|
12878
|
+
data: { manifest: input.manifest }
|
|
12879
|
+
};
|
|
12880
|
+
}
|
|
12881
|
+
async function git(cwd, args) {
|
|
12882
|
+
const result = await execFileAsync("git", args, {
|
|
12883
|
+
cwd,
|
|
12884
|
+
encoding: "buffer",
|
|
12885
|
+
maxBuffer: 50 * 1024 * 1024
|
|
12886
|
+
});
|
|
12887
|
+
return result.stdout.toString("utf8");
|
|
12888
|
+
}
|
|
12889
|
+
async function gitMaybe(cwd, args) {
|
|
12890
|
+
try {
|
|
12891
|
+
return await git(cwd, args);
|
|
12892
|
+
} catch {
|
|
12893
|
+
return "";
|
|
12894
|
+
}
|
|
12895
|
+
}
|
|
12896
|
+
async function isGitWorkspace(cwd) {
|
|
12897
|
+
return (await gitMaybe(cwd, ["rev-parse", "--is-inside-work-tree"])).trim() === "true";
|
|
12898
|
+
}
|
|
12899
|
+
async function isIgnored(cwd, rel) {
|
|
12900
|
+
try {
|
|
12901
|
+
await execFileAsync("git", ["check-ignore", "--quiet", "--", rel], { cwd });
|
|
12902
|
+
return true;
|
|
12903
|
+
} catch {
|
|
12904
|
+
return false;
|
|
12905
|
+
}
|
|
12906
|
+
}
|
|
12907
|
+
function splitNul(value) {
|
|
12908
|
+
return value.split("\0").filter(Boolean);
|
|
12909
|
+
}
|
|
12910
|
+
function stringArray(value) {
|
|
12911
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
12912
|
+
}
|
|
12913
|
+
var DEFAULT_HARD_DENY = [
|
|
12914
|
+
".git",
|
|
12915
|
+
".git/**",
|
|
12916
|
+
".env",
|
|
12917
|
+
".env.*",
|
|
12918
|
+
"**/.env",
|
|
12919
|
+
"**/.env.*",
|
|
12920
|
+
".ssh",
|
|
12921
|
+
".ssh/**"
|
|
12922
|
+
];
|
|
12923
|
+
function isAllowedRelativePath(rel, hardDeny) {
|
|
12924
|
+
if (path19.isAbsolute(rel) || rel.includes("\0")) return false;
|
|
12925
|
+
const normalized = path19.posix.normalize(rel.replaceAll(path19.sep, "/"));
|
|
12926
|
+
if (normalized === ".." || normalized.startsWith("../")) return false;
|
|
12927
|
+
return !hardDeny.some((pattern) => matchesDenyPattern(normalized, pattern));
|
|
12928
|
+
}
|
|
12929
|
+
function matchesDenyPattern(rel, pattern) {
|
|
12930
|
+
const normalized = pattern.replaceAll(path19.sep, "/");
|
|
12931
|
+
if (normalized.includes("*") || normalized.includes("?")) {
|
|
12932
|
+
return globToRegExp(normalized).test(rel);
|
|
12933
|
+
}
|
|
12934
|
+
if (normalized.endsWith("/**")) {
|
|
12935
|
+
const prefix = normalized.slice(0, -3);
|
|
12936
|
+
return rel === prefix || rel.startsWith(`${prefix}/`);
|
|
12937
|
+
}
|
|
12938
|
+
if (normalized.startsWith("**/")) {
|
|
12939
|
+
const suffix = normalized.slice(3);
|
|
12940
|
+
return rel === suffix || rel.endsWith(`/${suffix}`);
|
|
12941
|
+
}
|
|
12942
|
+
if (normalized.endsWith(".*")) {
|
|
12943
|
+
const prefix = normalized.slice(0, -1);
|
|
12944
|
+
return rel.startsWith(prefix);
|
|
12945
|
+
}
|
|
12946
|
+
return rel === normalized;
|
|
12947
|
+
}
|
|
12948
|
+
function globToRegExp(pattern) {
|
|
12949
|
+
let source = "^";
|
|
12950
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
12951
|
+
const char = pattern[i];
|
|
12952
|
+
const next = pattern[i + 1];
|
|
12953
|
+
if (char === "*") {
|
|
12954
|
+
if (next === "*") {
|
|
12955
|
+
const after = pattern[i + 2];
|
|
12956
|
+
if (after === "/") {
|
|
12957
|
+
source += "(?:.*/)?";
|
|
12958
|
+
i += 2;
|
|
12959
|
+
} else {
|
|
12960
|
+
source += ".*";
|
|
12961
|
+
i += 1;
|
|
12962
|
+
}
|
|
12963
|
+
} else {
|
|
12964
|
+
source += "[^/]*";
|
|
12965
|
+
}
|
|
12966
|
+
} else if (char === "?") {
|
|
12967
|
+
source += "[^/]";
|
|
12968
|
+
} else {
|
|
12969
|
+
source += escapeRegExp(char);
|
|
12970
|
+
}
|
|
12971
|
+
}
|
|
12972
|
+
source += "$";
|
|
12973
|
+
return new RegExp(source);
|
|
12974
|
+
}
|
|
12975
|
+
function escapeRegExp(value) {
|
|
12976
|
+
return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
12977
|
+
}
|
|
12978
|
+
function sha256(bytes) {
|
|
12979
|
+
return crypto3.createHash("sha256").update(bytes).digest("hex");
|
|
12980
|
+
}
|
|
12981
|
+
function joinObjectName(...parts) {
|
|
12982
|
+
return parts.map((part) => part.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
|
|
12983
|
+
}
|
|
12984
|
+
function safeObjectSegment(value) {
|
|
12985
|
+
const safe = value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
12986
|
+
return safe.length > 0 ? safe.slice(0, 160) : "artifact";
|
|
12987
|
+
}
|
|
12988
|
+
function contentTypeFor(filePath) {
|
|
12989
|
+
if (filePath.endsWith(".json")) return "application/json";
|
|
12990
|
+
if (filePath.endsWith(".diff") || filePath.endsWith(".patch")) {
|
|
12991
|
+
return "text/x-diff";
|
|
12992
|
+
}
|
|
12993
|
+
return "application/octet-stream";
|
|
12994
|
+
}
|
|
12995
|
+
|
|
12589
12996
|
// src/app/dashboard/remoteRunExecutor.ts
|
|
12590
12997
|
var DEFAULT_MARKETPLACE_SLUG = "lespaceman/athena-workflow-marketplace";
|
|
12591
12998
|
function parseRemoteRunSpec(value) {
|
|
@@ -12704,7 +13111,7 @@ function mergeRunSpecEnvIntoWorkflow(workflow, env) {
|
|
|
12704
13111
|
async function executeRemoteAssignment({
|
|
12705
13112
|
frame,
|
|
12706
13113
|
client,
|
|
12707
|
-
projectDir
|
|
13114
|
+
projectDir,
|
|
12708
13115
|
log = () => {
|
|
12709
13116
|
},
|
|
12710
13117
|
runExecFn = runExec,
|
|
@@ -12717,6 +13124,8 @@ async function executeRemoteAssignment({
|
|
|
12717
13124
|
resolveWorkflowInstallFn = resolveWorkflowInstall,
|
|
12718
13125
|
installWorkflowFromSourceFn = installWorkflowFromSource,
|
|
12719
13126
|
readGlobalConfigFn = readGlobalConfig,
|
|
13127
|
+
uploadArtifactObjectFn,
|
|
13128
|
+
dashboardFeedPublisher,
|
|
12720
13129
|
runStreamConnectTimeoutMs = 5e3
|
|
12721
13130
|
}) {
|
|
12722
13131
|
const lastTerminalFailureMessage = { current: null };
|
|
@@ -12746,7 +13155,15 @@ async function executeRemoteAssignment({
|
|
|
12746
13155
|
send("error", { message: "remote assignment missing prompt" });
|
|
12747
13156
|
return;
|
|
12748
13157
|
}
|
|
12749
|
-
|
|
13158
|
+
let artifactUploadSpec;
|
|
13159
|
+
try {
|
|
13160
|
+
artifactUploadSpec = parseArtifactUploadSpec(frame.runSpec);
|
|
13161
|
+
} catch (err) {
|
|
13162
|
+
send("error", {
|
|
13163
|
+
message: err instanceof Error ? err.message : String(err)
|
|
13164
|
+
});
|
|
13165
|
+
return;
|
|
13166
|
+
}
|
|
12750
13167
|
let runtimeConfig;
|
|
12751
13168
|
try {
|
|
12752
13169
|
const workflowOverride = ensureRemoteWorkflowInstalled({
|
|
@@ -12832,7 +13249,22 @@ async function executeRemoteAssignment({
|
|
|
12832
13249
|
signal: abortSignal,
|
|
12833
13250
|
stdout,
|
|
12834
13251
|
stderr,
|
|
12835
|
-
...decisionInbox ? { dashboardDecisionInbox: decisionInbox } : {}
|
|
13252
|
+
...decisionInbox ? { dashboardDecisionInbox: decisionInbox } : {},
|
|
13253
|
+
...dashboardFeedPublisher ? { dashboardFeedPublisher } : {},
|
|
13254
|
+
...artifactUploadSpec ? {
|
|
13255
|
+
beforeTerminalCompletion: async ({ result: result2, runId }) => {
|
|
13256
|
+
const artifactRunId = runId ?? frame.runId;
|
|
13257
|
+
const { feedEvent } = await captureAndUploadArtifacts({
|
|
13258
|
+
spec: artifactUploadSpec,
|
|
13259
|
+
projectDir,
|
|
13260
|
+
runId: artifactRunId,
|
|
13261
|
+
result: result2,
|
|
13262
|
+
now,
|
|
13263
|
+
...uploadArtifactObjectFn ? { uploadObject: uploadArtifactObjectFn } : {}
|
|
13264
|
+
});
|
|
13265
|
+
return [feedEvent];
|
|
13266
|
+
}
|
|
13267
|
+
} : {}
|
|
12836
13268
|
});
|
|
12837
13269
|
const failedCompletion = deferredFailedCompletion.current;
|
|
12838
13270
|
if (failedCompletion) {
|
|
@@ -12877,19 +13309,19 @@ async function executeRemoteAssignment({
|
|
|
12877
13309
|
}
|
|
12878
13310
|
|
|
12879
13311
|
// src/infra/config/attachmentMirror.ts
|
|
12880
|
-
import
|
|
12881
|
-
import
|
|
13312
|
+
import crypto4 from "crypto";
|
|
13313
|
+
import fs22 from "fs";
|
|
12882
13314
|
import os12 from "os";
|
|
12883
|
-
import
|
|
13315
|
+
import path20 from "path";
|
|
12884
13316
|
function attachmentMirrorPath(env = process.env) {
|
|
12885
13317
|
const home = env["HOME"] ?? os12.homedir();
|
|
12886
|
-
return
|
|
13318
|
+
return path20.join(home, ".config", "athena", "attachments.json");
|
|
12887
13319
|
}
|
|
12888
13320
|
function readAttachmentMirror(env = process.env) {
|
|
12889
13321
|
const file = attachmentMirrorPath(env);
|
|
12890
13322
|
let raw;
|
|
12891
13323
|
try {
|
|
12892
|
-
raw =
|
|
13324
|
+
raw = fs22.readFileSync(file, "utf-8");
|
|
12893
13325
|
} catch (err) {
|
|
12894
13326
|
if (err.code === "ENOENT") return null;
|
|
12895
13327
|
throw err;
|
|
@@ -12913,29 +13345,29 @@ function readAttachmentMirror(env = process.env) {
|
|
|
12913
13345
|
function writeAttachmentMirror(mirror, env = process.env) {
|
|
12914
13346
|
const validated = parseAttachmentMirror(mirror);
|
|
12915
13347
|
const file = attachmentMirrorPath(env);
|
|
12916
|
-
const dir =
|
|
12917
|
-
|
|
12918
|
-
const tmp = `${file}.${process.pid}.${
|
|
12919
|
-
const fd =
|
|
13348
|
+
const dir = path20.dirname(file);
|
|
13349
|
+
fs22.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
13350
|
+
const tmp = `${file}.${process.pid}.${crypto4.randomBytes(4).toString("hex")}.tmp`;
|
|
13351
|
+
const fd = fs22.openSync(tmp, "w", 384);
|
|
12920
13352
|
try {
|
|
12921
|
-
|
|
12922
|
-
|
|
13353
|
+
fs22.writeSync(fd, JSON.stringify(validated, null, 2) + "\n");
|
|
13354
|
+
fs22.fsyncSync(fd);
|
|
12923
13355
|
} finally {
|
|
12924
|
-
|
|
13356
|
+
fs22.closeSync(fd);
|
|
12925
13357
|
}
|
|
12926
13358
|
try {
|
|
12927
|
-
|
|
13359
|
+
fs22.renameSync(tmp, file);
|
|
12928
13360
|
} catch (err) {
|
|
12929
13361
|
try {
|
|
12930
|
-
|
|
13362
|
+
fs22.unlinkSync(tmp);
|
|
12931
13363
|
} catch {
|
|
12932
13364
|
}
|
|
12933
13365
|
throw err;
|
|
12934
13366
|
}
|
|
12935
13367
|
if (process.platform !== "win32") {
|
|
12936
13368
|
try {
|
|
12937
|
-
|
|
12938
|
-
|
|
13369
|
+
fs22.chmodSync(dir, 448);
|
|
13370
|
+
fs22.chmodSync(file, 384);
|
|
12939
13371
|
} catch {
|
|
12940
13372
|
}
|
|
12941
13373
|
}
|
|
@@ -12943,7 +13375,7 @@ function writeAttachmentMirror(mirror, env = process.env) {
|
|
|
12943
13375
|
function removeAttachmentMirror(env = process.env) {
|
|
12944
13376
|
const file = attachmentMirrorPath(env);
|
|
12945
13377
|
try {
|
|
12946
|
-
|
|
13378
|
+
fs22.unlinkSync(file);
|
|
12947
13379
|
} catch (err) {
|
|
12948
13380
|
if (err.code !== "ENOENT") throw err;
|
|
12949
13381
|
}
|
|
@@ -13209,6 +13641,7 @@ function createDashboardPairedExecution(options) {
|
|
|
13209
13641
|
});
|
|
13210
13642
|
const maxConcurrentRuns = options.maxConcurrentRuns ?? DEFAULT_MAX_CONCURRENT_RUNS;
|
|
13211
13643
|
const runHistoryLimit = options.runHistoryLimit ?? DEFAULT_RUN_HISTORY_LIMIT;
|
|
13644
|
+
const pairedFeedPublisher = options.pairedFeedPublisher;
|
|
13212
13645
|
const now = options.now ?? (() => Date.now());
|
|
13213
13646
|
let completedRuns = 0;
|
|
13214
13647
|
const active = /* @__PURE__ */ new Map();
|
|
@@ -13248,7 +13681,7 @@ function createDashboardPairedExecution(options) {
|
|
|
13248
13681
|
entry.record.status = "cancelled";
|
|
13249
13682
|
entry.controller.abort();
|
|
13250
13683
|
}
|
|
13251
|
-
function handleAssignment(frame) {
|
|
13684
|
+
function handleAssignment(frame, input = {}) {
|
|
13252
13685
|
if (active.has(frame.runId)) {
|
|
13253
13686
|
const rejection = {
|
|
13254
13687
|
reason: "duplicate",
|
|
@@ -13279,10 +13712,11 @@ function createDashboardPairedExecution(options) {
|
|
|
13279
13712
|
const promise = executor({
|
|
13280
13713
|
frame,
|
|
13281
13714
|
client,
|
|
13282
|
-
projectDir,
|
|
13715
|
+
projectDir: input.projectDir ?? projectDir,
|
|
13283
13716
|
log,
|
|
13284
13717
|
abortSignal: controller.signal,
|
|
13285
|
-
decisionInbox
|
|
13718
|
+
decisionInbox,
|
|
13719
|
+
...pairedFeedPublisher ? { dashboardFeedPublisher: pairedFeedPublisher } : {}
|
|
13286
13720
|
}).then(() => {
|
|
13287
13721
|
if (record.status === "running") record.status = "completed";
|
|
13288
13722
|
}).catch((err) => {
|
|
@@ -13323,8 +13757,8 @@ function createDashboardPairedExecution(options) {
|
|
|
13323
13757
|
}
|
|
13324
13758
|
return false;
|
|
13325
13759
|
},
|
|
13326
|
-
admitAssignment(frame) {
|
|
13327
|
-
return handleAssignment(frame);
|
|
13760
|
+
admitAssignment(frame, input) {
|
|
13761
|
+
return handleAssignment(frame, input);
|
|
13328
13762
|
},
|
|
13329
13763
|
rejectAssignment,
|
|
13330
13764
|
snapshot() {
|
|
@@ -13352,10 +13786,125 @@ function createDashboardPairedExecution(options) {
|
|
|
13352
13786
|
};
|
|
13353
13787
|
}
|
|
13354
13788
|
|
|
13789
|
+
// src/app/dashboard/remoteWorkspaceResolver.ts
|
|
13790
|
+
import fs23 from "fs";
|
|
13791
|
+
import os13 from "os";
|
|
13792
|
+
import path21 from "path";
|
|
13793
|
+
function resolveRemoteWorkspace(frame, options = {}) {
|
|
13794
|
+
const spec = parseRemoteRunSpec(frame.runSpec);
|
|
13795
|
+
if (!spec) {
|
|
13796
|
+
return {
|
|
13797
|
+
kind: "rejected",
|
|
13798
|
+
rejection: {
|
|
13799
|
+
reason: "workspace_unresolved",
|
|
13800
|
+
message: "remote assignment missing prompt"
|
|
13801
|
+
}
|
|
13802
|
+
};
|
|
13803
|
+
}
|
|
13804
|
+
if (spec.projectDir) {
|
|
13805
|
+
return validateProjectDir(spec.projectDir, options.env);
|
|
13806
|
+
}
|
|
13807
|
+
const sessionId = spec.athenaSessionId ?? spec.sessionId;
|
|
13808
|
+
const runnerId = frame.runnerId ?? "legacy";
|
|
13809
|
+
const deploymentSlug = deploymentSlugFromUrl(options.dashboardUrl);
|
|
13810
|
+
const stateDir = daemonStatePaths(options.env).dir;
|
|
13811
|
+
const projectDir = sessionId ? path21.join(
|
|
13812
|
+
stateDir,
|
|
13813
|
+
"remote-workspaces",
|
|
13814
|
+
deploymentSlug,
|
|
13815
|
+
sanitizePathSegment(runnerId),
|
|
13816
|
+
"sessions",
|
|
13817
|
+
sanitizePathSegment(sessionId)
|
|
13818
|
+
) : path21.join(
|
|
13819
|
+
stateDir,
|
|
13820
|
+
"remote-workspaces",
|
|
13821
|
+
deploymentSlug,
|
|
13822
|
+
sanitizePathSegment(runnerId),
|
|
13823
|
+
"runs",
|
|
13824
|
+
sanitizePathSegment(frame.runId)
|
|
13825
|
+
);
|
|
13826
|
+
try {
|
|
13827
|
+
fs23.mkdirSync(projectDir, { recursive: true, mode: 448 });
|
|
13828
|
+
if (process.platform !== "win32") {
|
|
13829
|
+
try {
|
|
13830
|
+
fs23.chmodSync(projectDir, 448);
|
|
13831
|
+
} catch {
|
|
13832
|
+
}
|
|
13833
|
+
}
|
|
13834
|
+
} catch (err) {
|
|
13835
|
+
return {
|
|
13836
|
+
kind: "rejected",
|
|
13837
|
+
rejection: {
|
|
13838
|
+
reason: "workspace_unresolved",
|
|
13839
|
+
message: `failed to create remote workspace: ${err instanceof Error ? err.message : String(err)}`
|
|
13840
|
+
}
|
|
13841
|
+
};
|
|
13842
|
+
}
|
|
13843
|
+
return validateProjectDir(projectDir, options.env);
|
|
13844
|
+
}
|
|
13845
|
+
function validateProjectDir(projectDir, env = process.env) {
|
|
13846
|
+
const resolved = path21.resolve(projectDir);
|
|
13847
|
+
if (!path21.isAbsolute(projectDir)) {
|
|
13848
|
+
return {
|
|
13849
|
+
kind: "rejected",
|
|
13850
|
+
rejection: {
|
|
13851
|
+
reason: "workspace_invalid",
|
|
13852
|
+
message: `remote workspace must be an absolute path: ${projectDir}`
|
|
13853
|
+
}
|
|
13854
|
+
};
|
|
13855
|
+
}
|
|
13856
|
+
const home = path21.resolve(env["HOME"] ?? os13.homedir());
|
|
13857
|
+
if (resolved === home) {
|
|
13858
|
+
return {
|
|
13859
|
+
kind: "rejected",
|
|
13860
|
+
rejection: {
|
|
13861
|
+
reason: "workspace_invalid",
|
|
13862
|
+
message: "remote workspace cannot be the user home directory"
|
|
13863
|
+
}
|
|
13864
|
+
};
|
|
13865
|
+
}
|
|
13866
|
+
let stat;
|
|
13867
|
+
try {
|
|
13868
|
+
stat = fs23.statSync(resolved);
|
|
13869
|
+
} catch {
|
|
13870
|
+
return {
|
|
13871
|
+
kind: "rejected",
|
|
13872
|
+
rejection: {
|
|
13873
|
+
reason: "workspace_invalid",
|
|
13874
|
+
message: `remote workspace does not exist: ${resolved}`
|
|
13875
|
+
}
|
|
13876
|
+
};
|
|
13877
|
+
}
|
|
13878
|
+
if (!stat.isDirectory()) {
|
|
13879
|
+
return {
|
|
13880
|
+
kind: "rejected",
|
|
13881
|
+
rejection: {
|
|
13882
|
+
reason: "workspace_invalid",
|
|
13883
|
+
message: `remote workspace is not a directory: ${resolved}`
|
|
13884
|
+
}
|
|
13885
|
+
};
|
|
13886
|
+
}
|
|
13887
|
+
return { kind: "resolved", projectDir: resolved };
|
|
13888
|
+
}
|
|
13889
|
+
function deploymentSlugFromUrl(dashboardUrl) {
|
|
13890
|
+
if (!dashboardUrl) return "unknown-dashboard";
|
|
13891
|
+
try {
|
|
13892
|
+
const url = new URL(dashboardUrl);
|
|
13893
|
+
return sanitizePathSegment(url.host);
|
|
13894
|
+
} catch {
|
|
13895
|
+
return sanitizePathSegment(dashboardUrl);
|
|
13896
|
+
}
|
|
13897
|
+
}
|
|
13898
|
+
function sanitizePathSegment(value) {
|
|
13899
|
+
const cleaned = value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "");
|
|
13900
|
+
return cleaned.length > 0 ? cleaned : "unknown";
|
|
13901
|
+
}
|
|
13902
|
+
|
|
13355
13903
|
// src/app/dashboard/dashboardAssignmentIntake.ts
|
|
13356
13904
|
function createDashboardAssignmentIntake(options) {
|
|
13357
13905
|
const log = options.log ?? (() => {
|
|
13358
13906
|
});
|
|
13907
|
+
const resolveWorkspace = options.resolveWorkspace ?? ((frame) => resolveRemoteWorkspace(frame));
|
|
13359
13908
|
const pending = [];
|
|
13360
13909
|
let ready = false;
|
|
13361
13910
|
function handle(frame) {
|
|
@@ -13371,7 +13920,18 @@ function createDashboardAssignmentIntake(options) {
|
|
|
13371
13920
|
});
|
|
13372
13921
|
return;
|
|
13373
13922
|
}
|
|
13374
|
-
const
|
|
13923
|
+
const workspace = resolveWorkspace(frame);
|
|
13924
|
+
if (workspace.kind === "rejected") {
|
|
13925
|
+
options.execution.rejectAssignment(frame.runId, workspace.rejection);
|
|
13926
|
+
options.client.sendAssignmentRejected({
|
|
13927
|
+
runId: frame.runId,
|
|
13928
|
+
...workspace.rejection
|
|
13929
|
+
});
|
|
13930
|
+
return;
|
|
13931
|
+
}
|
|
13932
|
+
const outcome = options.execution.admitAssignment(frame, {
|
|
13933
|
+
projectDir: workspace.projectDir
|
|
13934
|
+
});
|
|
13375
13935
|
if (outcome.kind === "accepted") {
|
|
13376
13936
|
options.client.sendAssignmentAccepted(frame.runId);
|
|
13377
13937
|
return;
|
|
@@ -13487,6 +14047,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
13487
14047
|
executor,
|
|
13488
14048
|
projectDir,
|
|
13489
14049
|
decisionInbox,
|
|
14050
|
+
pairedFeedPublisher,
|
|
13490
14051
|
log,
|
|
13491
14052
|
maxConcurrentRuns,
|
|
13492
14053
|
now,
|
|
@@ -13511,7 +14072,8 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
13511
14072
|
}
|
|
13512
14073
|
},
|
|
13513
14074
|
execution: pairedExecution,
|
|
13514
|
-
log
|
|
14075
|
+
log,
|
|
14076
|
+
resolveWorkspace: (frame) => resolveRemoteWorkspace(frame, { dashboardUrl: currentDashboardUrl })
|
|
13515
14077
|
});
|
|
13516
14078
|
function nextReconnectDelay() {
|
|
13517
14079
|
if (reconnectDelays.length === 0) return 0;
|
|
@@ -13659,9 +14221,9 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
13659
14221
|
next.close("attachment reconciliation failed");
|
|
13660
14222
|
throw err;
|
|
13661
14223
|
}
|
|
13662
|
-
assignmentIntake.markReady();
|
|
13663
14224
|
currentInstanceId = token.instanceId;
|
|
13664
14225
|
currentDashboardUrl = config.dashboardUrl;
|
|
14226
|
+
assignmentIntake.markReady();
|
|
13665
14227
|
reconnectAttempt = 0;
|
|
13666
14228
|
scheduleRefresh(token.expiresInSec);
|
|
13667
14229
|
pairedFeedPublisher.attachTransport(next);
|
|
@@ -13729,18 +14291,18 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
13729
14291
|
}
|
|
13730
14292
|
|
|
13731
14293
|
// src/infra/daemon/pidLock.ts
|
|
13732
|
-
import
|
|
14294
|
+
import fs24 from "fs";
|
|
13733
14295
|
function acquirePidLock(pidPath) {
|
|
13734
14296
|
const ownPid = process.pid;
|
|
13735
14297
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
13736
14298
|
try {
|
|
13737
|
-
const fd =
|
|
14299
|
+
const fd = fs24.openSync(pidPath, "wx", 384);
|
|
13738
14300
|
try {
|
|
13739
|
-
|
|
14301
|
+
fs24.writeSync(fd, `${ownPid}
|
|
13740
14302
|
`);
|
|
13741
|
-
|
|
14303
|
+
fs24.fsyncSync(fd);
|
|
13742
14304
|
} finally {
|
|
13743
|
-
|
|
14305
|
+
fs24.closeSync(fd);
|
|
13744
14306
|
}
|
|
13745
14307
|
return makeHandle(pidPath, ownPid);
|
|
13746
14308
|
} catch (err) {
|
|
@@ -13754,7 +14316,7 @@ function acquirePidLock(pidPath) {
|
|
|
13754
14316
|
}
|
|
13755
14317
|
if (existing.state === "stale") {
|
|
13756
14318
|
try {
|
|
13757
|
-
|
|
14319
|
+
fs24.unlinkSync(pidPath);
|
|
13758
14320
|
} catch (err) {
|
|
13759
14321
|
if (err.code !== "ENOENT") throw err;
|
|
13760
14322
|
}
|
|
@@ -13768,7 +14330,7 @@ function acquirePidLock(pidPath) {
|
|
|
13768
14330
|
function readPidLock(pidPath) {
|
|
13769
14331
|
let raw;
|
|
13770
14332
|
try {
|
|
13771
|
-
raw =
|
|
14333
|
+
raw = fs24.readFileSync(pidPath, "utf-8");
|
|
13772
14334
|
} catch (err) {
|
|
13773
14335
|
if (err.code === "ENOENT") {
|
|
13774
14336
|
return { state: "absent" };
|
|
@@ -13792,9 +14354,9 @@ function makeHandle(pidPath, pid) {
|
|
|
13792
14354
|
if (released) return;
|
|
13793
14355
|
released = true;
|
|
13794
14356
|
try {
|
|
13795
|
-
const raw =
|
|
14357
|
+
const raw = fs24.readFileSync(pidPath, "utf-8").trim();
|
|
13796
14358
|
if (raw === String(pid)) {
|
|
13797
|
-
|
|
14359
|
+
fs24.unlinkSync(pidPath);
|
|
13798
14360
|
}
|
|
13799
14361
|
} catch (err) {
|
|
13800
14362
|
if (err.code !== "ENOENT") {
|
|
@@ -13820,7 +14382,7 @@ function isProcessAlive(pid) {
|
|
|
13820
14382
|
}
|
|
13821
14383
|
|
|
13822
14384
|
// src/infra/daemon/udsIpc.ts
|
|
13823
|
-
import
|
|
14385
|
+
import fs25 from "fs";
|
|
13824
14386
|
import net2 from "net";
|
|
13825
14387
|
|
|
13826
14388
|
// src/infra/daemon/udsFrameCodec.ts
|
|
@@ -13865,7 +14427,7 @@ async function startUdsServer(socketPath, handler, log) {
|
|
|
13865
14427
|
});
|
|
13866
14428
|
if (process.platform !== "win32") {
|
|
13867
14429
|
try {
|
|
13868
|
-
|
|
14430
|
+
fs25.chmodSync(socketPath, 384);
|
|
13869
14431
|
} catch {
|
|
13870
14432
|
}
|
|
13871
14433
|
}
|
|
@@ -13875,7 +14437,7 @@ async function startUdsServer(socketPath, handler, log) {
|
|
|
13875
14437
|
server.close(() => resolve());
|
|
13876
14438
|
});
|
|
13877
14439
|
try {
|
|
13878
|
-
|
|
14440
|
+
fs25.unlinkSync(socketPath);
|
|
13879
14441
|
} catch (err) {
|
|
13880
14442
|
if (err.code !== "ENOENT") {
|
|
13881
14443
|
}
|
|
@@ -13993,7 +14555,7 @@ async function sendUdsRequest(socketPath, request, options = {}) {
|
|
|
13993
14555
|
async function unlinkStaleSocket(socketPath) {
|
|
13994
14556
|
let stat;
|
|
13995
14557
|
try {
|
|
13996
|
-
stat =
|
|
14558
|
+
stat = fs25.statSync(socketPath);
|
|
13997
14559
|
} catch (err) {
|
|
13998
14560
|
if (err.code === "ENOENT") return;
|
|
13999
14561
|
throw err;
|
|
@@ -14020,7 +14582,7 @@ async function unlinkStaleSocket(socketPath) {
|
|
|
14020
14582
|
`uds path ${socketPath} is in use by another process; aborting`
|
|
14021
14583
|
);
|
|
14022
14584
|
}
|
|
14023
|
-
|
|
14585
|
+
fs25.unlinkSync(socketPath);
|
|
14024
14586
|
}
|
|
14025
14587
|
|
|
14026
14588
|
export {
|
|
@@ -14098,4 +14660,4 @@ export {
|
|
|
14098
14660
|
startUdsServer,
|
|
14099
14661
|
sendUdsRequest
|
|
14100
14662
|
};
|
|
14101
|
-
//# sourceMappingURL=chunk-
|
|
14663
|
+
//# sourceMappingURL=chunk-UKMVFGY2.js.map
|
package/dist/cli.js
CHANGED
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
writeAttachmentMirror,
|
|
75
75
|
writeGatewayClientConfig,
|
|
76
76
|
wsClientOptionsForEndpoint
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-UKMVFGY2.js";
|
|
78
78
|
import {
|
|
79
79
|
generateId as generateId2
|
|
80
80
|
} from "./chunk-BTKQ67RE.js";
|
|
@@ -5910,6 +5910,13 @@ var gatewayFunctionCompleted = defaultRenderer(
|
|
|
5910
5910
|
var gatewayFunctionFailed = defaultRenderer(
|
|
5911
5911
|
(event) => `fn ${event.data.reason}: ${event.data.function_name} \u2014 ${event.data.error_message}`
|
|
5912
5912
|
);
|
|
5913
|
+
var artifactsManifest = defaultRenderer(
|
|
5914
|
+
(event) => {
|
|
5915
|
+
const manifest = event.data.manifest;
|
|
5916
|
+
const count = Array.isArray(manifest.entries) ? manifest.entries.length : 0;
|
|
5917
|
+
return `artifacts manifest (${count} item${count === 1 ? "" : "s"})`;
|
|
5918
|
+
}
|
|
5919
|
+
);
|
|
5913
5920
|
var RENDERERS = {
|
|
5914
5921
|
"session.start": sessionStart,
|
|
5915
5922
|
"session.end": sessionEnd,
|
|
@@ -5968,7 +5975,8 @@ var RENDERERS = {
|
|
|
5968
5975
|
"channel.chat.outbound": channelChatOutbound,
|
|
5969
5976
|
"gateway.function.invoked": gatewayFunctionInvoked,
|
|
5970
5977
|
"gateway.function.completed": gatewayFunctionCompleted,
|
|
5971
|
-
"gateway.function.failed": gatewayFunctionFailed
|
|
5978
|
+
"gateway.function.failed": gatewayFunctionFailed,
|
|
5979
|
+
"artifacts.manifest": artifactsManifest
|
|
5972
5980
|
};
|
|
5973
5981
|
function rendererFor(event) {
|
|
5974
5982
|
return RENDERERS[event.kind];
|
|
@@ -6814,6 +6822,7 @@ function resolveEventToolColumn(event) {
|
|
|
6814
6822
|
case "gateway.function.invoked":
|
|
6815
6823
|
case "gateway.function.completed":
|
|
6816
6824
|
case "gateway.function.failed":
|
|
6825
|
+
case "artifacts.manifest":
|
|
6817
6826
|
return "";
|
|
6818
6827
|
}
|
|
6819
6828
|
}
|
|
@@ -8548,7 +8557,8 @@ function renderDetailLines(event, width, pairedPostEvent, theme = darkTheme) {
|
|
|
8548
8557
|
case "channel.chat.outbound":
|
|
8549
8558
|
case "gateway.function.invoked":
|
|
8550
8559
|
case "gateway.function.completed":
|
|
8551
|
-
case "gateway.function.failed":
|
|
8560
|
+
case "gateway.function.failed":
|
|
8561
|
+
case "artifacts.manifest": {
|
|
8552
8562
|
const header = buildCompactHeader(event, width, { theme });
|
|
8553
8563
|
const json = JSON.stringify(event.raw ?? event.data, null, 2);
|
|
8554
8564
|
const content = highlightCode(json, cw, "json");
|
|
@@ -15695,7 +15705,7 @@ var cachedVersion = null;
|
|
|
15695
15705
|
function readPackageVersion() {
|
|
15696
15706
|
if (cachedVersion !== null) return cachedVersion;
|
|
15697
15707
|
try {
|
|
15698
|
-
const injected = "0.5.
|
|
15708
|
+
const injected = "0.5.7";
|
|
15699
15709
|
if (typeof injected === "string" && injected.length > 0) {
|
|
15700
15710
|
cachedVersion = injected;
|
|
15701
15711
|
return cachedVersion;
|
package/dist/dashboard-daemon.js
CHANGED