@drisp/cli 0.5.6 → 0.5.8

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.
@@ -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.6";
2244
+ const injected = "0.5.8";
2245
2245
  if (typeof injected === "string" && injected.length > 0) {
2246
2246
  cachedVersion = injected;
2247
2247
  return cachedVersion;
@@ -5208,16 +5208,21 @@ function translateServerRequest(msg) {
5208
5208
  networkContext: params.networkApprovalContext && typeof params.networkApprovalContext === "object" ? {
5209
5209
  host: params.networkApprovalContext.host,
5210
5210
  protocol: params.networkApprovalContext.protocol
5211
- } : void 0
5211
+ } : void 0,
5212
+ toolUseId: params.itemId
5212
5213
  }
5213
5214
  );
5214
5215
  }
5215
5216
  case FILE_CHANGE_REQUEST_APPROVAL: {
5216
5217
  const params = msg.params;
5217
- return permissionRequestEvent("Edit", {
5218
- reason: params.reason,
5219
- grantRoot: params.grantRoot
5220
- });
5218
+ return permissionRequestEvent(
5219
+ "Edit",
5220
+ {
5221
+ reason: params.reason,
5222
+ grantRoot: params.grantRoot
5223
+ },
5224
+ { toolUseId: params.itemId }
5225
+ );
5221
5226
  }
5222
5227
  case PERMISSIONS_REQUEST_APPROVAL: {
5223
5228
  const params = msg.params;
@@ -7700,6 +7705,8 @@ function generateNeutralTitle(event, g) {
7700
7705
  return truncate(
7701
7706
  `Cloud fn \u2717 ${event.data.function_name}: ${event.data.error_message}`
7702
7707
  );
7708
+ case "artifacts.manifest":
7709
+ return "Artifacts manifest";
7703
7710
  }
7704
7711
  }
7705
7712
 
@@ -11665,6 +11672,7 @@ async function runExec(options) {
11665
11672
  let mappedFinalMessage = null;
11666
11673
  let adapterSessionId = null;
11667
11674
  let activeRunId = null;
11675
+ let beforeTerminalCompletionRan = false;
11668
11676
  let store;
11669
11677
  try {
11670
11678
  store = sessionStoreFactory({
@@ -11787,6 +11795,57 @@ async function runExec(options) {
11787
11795
  feedEvents
11788
11796
  });
11789
11797
  }
11798
+ const runBeforeTerminalCompletion = async () => {
11799
+ if (beforeTerminalCompletionRan || latch.hasFailure() || !options.beforeTerminalCompletion) {
11800
+ return;
11801
+ }
11802
+ beforeTerminalCompletionRan = true;
11803
+ const resolved = resolveFinalMessage({
11804
+ streamMessage: streamFinalMessage,
11805
+ mappedMessage: mappedFinalMessage
11806
+ });
11807
+ const provisionalResult = {
11808
+ success: true,
11809
+ exitCode: EXEC_EXIT_CODE.SUCCESS,
11810
+ athenaSessionId: options.ephemeral ? null : athenaSessionId,
11811
+ adapterSessionId,
11812
+ finalMessage: resolved.message,
11813
+ tokens: cumulativeTokens,
11814
+ durationMs: Math.max(0, now() - startTs)
11815
+ };
11816
+ try {
11817
+ const feedEvents = await options.beforeTerminalCompletion({
11818
+ result: provisionalResult,
11819
+ runId: activeRunId
11820
+ });
11821
+ if (feedEvents && feedEvents.length > 0) {
11822
+ publishFeedEvents(feedEvents);
11823
+ }
11824
+ } catch (error) {
11825
+ latch.register({
11826
+ kind: "output",
11827
+ message: `Artifact upload failed: ${error instanceof Error ? error.message : String(error)}`
11828
+ });
11829
+ }
11830
+ };
11831
+ const writeLastMessageBeforeTerminalCompletion = async () => {
11832
+ if (latch.hasFailure() || !options.outputLastMessagePath) return;
11833
+ const resolved = resolveFinalMessage({
11834
+ streamMessage: streamFinalMessage,
11835
+ mappedMessage: mappedFinalMessage
11836
+ });
11837
+ try {
11838
+ await output.writeLastMessage(
11839
+ options.outputLastMessagePath,
11840
+ resolved.message
11841
+ );
11842
+ } catch (error) {
11843
+ latch.register({
11844
+ kind: "output",
11845
+ message: `Failed writing --output-last-message: ${error instanceof Error ? error.message : String(error)}`
11846
+ });
11847
+ }
11848
+ };
11790
11849
  const unsubscribeEvent = runtime.onEvent((runtimeEvent) => {
11791
11850
  adapterSessionId = runtimeEvent.sessionId;
11792
11851
  if (runtimeEvent.sessionId && activeRunId && !linkedAdapterSessions.has(runtimeEvent.sessionId)) {
@@ -11956,6 +12015,8 @@ async function runExec(options) {
11956
12015
  if (dashboardDecisionTimer) {
11957
12016
  clearInterval(dashboardDecisionTimer);
11958
12017
  }
12018
+ await writeLastMessageBeforeTerminalCompletion();
12019
+ await runBeforeTerminalCompletion();
11959
12020
  await sessionController.kill();
11960
12021
  unsubscribeEvent();
11961
12022
  unsubscribeDecision();
@@ -11974,19 +12035,6 @@ async function runExec(options) {
11974
12035
  output.warn(warning);
11975
12036
  output.emitJsonEvent("exec.warning", { message: warning });
11976
12037
  }
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
12038
  const failure = latch.current();
11991
12039
  const exitCode = exitCodeFromFailure(failure);
11992
12040
  const success = exitCode === EXEC_EXIT_CODE.SUCCESS;
@@ -12586,6 +12634,370 @@ async function createRemoteRunEventPublisher({
12586
12634
  };
12587
12635
  }
12588
12636
 
12637
+ // src/app/dashboard/artifactCapture.ts
12638
+ import { execFile } from "child_process";
12639
+ import crypto3 from "crypto";
12640
+ import fs21 from "fs/promises";
12641
+ import path19 from "path";
12642
+ import { promisify } from "util";
12643
+ var execFileAsync = promisify(execFile);
12644
+ function parseArtifactUploadSpec(value) {
12645
+ if (typeof value !== "object" || value === null) return null;
12646
+ const obj = value;
12647
+ const hasArtifactUpload = Object.hasOwn(obj, "artifactUpload");
12648
+ const hasArtifacts = Object.hasOwn(obj, "artifacts");
12649
+ if (!hasArtifactUpload && !hasArtifacts) return null;
12650
+ const candidate = hasArtifactUpload ? obj["artifactUpload"] : obj["artifacts"];
12651
+ if (typeof candidate !== "object" || candidate === null) {
12652
+ throw new Error("artifact upload spec must be an object");
12653
+ }
12654
+ const spec = candidate;
12655
+ const bucket = spec["bucket"];
12656
+ const prefix = spec["prefix"];
12657
+ const normalizedPrefix = typeof prefix === "string" ? prefix.replace(/^\/+|\/+$/g, "") : null;
12658
+ const accessToken = spec["accessToken"] ?? (typeof spec["credentials"] === "object" && spec["credentials"] !== null ? spec["credentials"]["accessToken"] : void 0);
12659
+ if (typeof bucket !== "string" || bucket.length === 0 || !normalizedPrefix || typeof accessToken !== "string" || accessToken.length === 0) {
12660
+ throw new Error(
12661
+ "artifact upload spec must include bucket, prefix, and accessToken"
12662
+ );
12663
+ }
12664
+ return {
12665
+ bucket,
12666
+ prefix: normalizedPrefix,
12667
+ accessToken,
12668
+ includeIgnored: stringArray(spec["includeIgnored"]),
12669
+ hardDeny: [...DEFAULT_HARD_DENY, ...stringArray(spec["hardDeny"])]
12670
+ };
12671
+ }
12672
+ async function captureAndUploadArtifacts(input) {
12673
+ const now = input.now ?? Date.now;
12674
+ const uploadObject = input.uploadObject ?? uploadGcsObject;
12675
+ const payloads = await collectArtifactPayloads({
12676
+ projectDir: input.projectDir,
12677
+ includeIgnored: input.spec.includeIgnored,
12678
+ hardDeny: input.spec.hardDeny
12679
+ });
12680
+ const entries = [];
12681
+ for (const [idx, payload] of payloads.entries()) {
12682
+ const id = `${String(idx + 1).padStart(4, "0")}-${safeObjectSegment(
12683
+ payload.kind
12684
+ )}`;
12685
+ const object = joinObjectName(
12686
+ input.spec.prefix,
12687
+ "payloads",
12688
+ `${id}-${safeObjectSegment(payload.path)}`
12689
+ );
12690
+ await uploadObject({
12691
+ bucket: input.spec.bucket,
12692
+ objectName: object,
12693
+ body: payload.bytes,
12694
+ contentType: contentTypeFor(payload.path),
12695
+ accessToken: input.spec.accessToken
12696
+ });
12697
+ entries.push({
12698
+ id,
12699
+ kind: payload.kind,
12700
+ path: payload.path,
12701
+ object,
12702
+ size: payload.bytes.byteLength,
12703
+ sha256: sha256(payload.bytes)
12704
+ });
12705
+ }
12706
+ const manifestObject = joinObjectName(input.spec.prefix, "manifest.json");
12707
+ const manifest = {
12708
+ version: 1,
12709
+ runId: input.runId,
12710
+ athenaSessionId: input.result.athenaSessionId,
12711
+ adapterSessionId: input.result.adapterSessionId,
12712
+ createdAt: new Date(now()).toISOString(),
12713
+ entries,
12714
+ objects: {
12715
+ bucket: input.spec.bucket,
12716
+ prefix: input.spec.prefix,
12717
+ manifest: manifestObject
12718
+ }
12719
+ };
12720
+ await uploadObject({
12721
+ bucket: input.spec.bucket,
12722
+ objectName: manifestObject,
12723
+ body: Buffer.from(`${JSON.stringify(manifest, null, 2)}
12724
+ `),
12725
+ contentType: "application/json",
12726
+ accessToken: input.spec.accessToken
12727
+ });
12728
+ return {
12729
+ manifest,
12730
+ feedEvent: makeArtifactManifestFeedEvent({
12731
+ manifest,
12732
+ result: input.result,
12733
+ runId: input.runId,
12734
+ ts: now()
12735
+ })
12736
+ };
12737
+ }
12738
+ async function collectArtifactPayloads(input) {
12739
+ const hardDeny = input.hardDeny ?? DEFAULT_HARD_DENY;
12740
+ const payloads = [];
12741
+ if (!await isGitWorkspace(input.projectDir)) {
12742
+ return payloads;
12743
+ }
12744
+ const trackedDiff = await gitDiffForAllowedPaths({
12745
+ projectDir: input.projectDir,
12746
+ nameArgs: ["diff", "--name-only", "-z", "--"],
12747
+ diffArgs: ["diff", "--binary", "--"],
12748
+ hardDeny
12749
+ });
12750
+ if (trackedDiff.length > 0) {
12751
+ payloads.push({
12752
+ kind: "tracked_diff",
12753
+ path: "git/tracked.diff",
12754
+ bytes: Buffer.from(trackedDiff)
12755
+ });
12756
+ }
12757
+ const stagedDiff = await gitDiffForAllowedPaths({
12758
+ projectDir: input.projectDir,
12759
+ nameArgs: ["diff", "--name-only", "-z", "--cached", "--"],
12760
+ diffArgs: ["diff", "--binary", "--cached", "--"],
12761
+ hardDeny
12762
+ });
12763
+ if (stagedDiff.length > 0) {
12764
+ payloads.push({
12765
+ kind: "staged_diff",
12766
+ path: "git/staged.diff",
12767
+ bytes: Buffer.from(stagedDiff)
12768
+ });
12769
+ }
12770
+ const upstream = await gitMaybe(input.projectDir, [
12771
+ "rev-parse",
12772
+ "--abbrev-ref",
12773
+ "--symbolic-full-name",
12774
+ "@{u}"
12775
+ ]);
12776
+ if (upstream.trim().length > 0) {
12777
+ const range = `${upstream.trim()}..HEAD`;
12778
+ const commits = await gitDiffForAllowedPaths({
12779
+ projectDir: input.projectDir,
12780
+ nameArgs: ["diff", "--name-only", "-z", range, "--"],
12781
+ diffArgs: ["format-patch", "--stdout", range, "--"],
12782
+ hardDeny
12783
+ });
12784
+ if (commits.length > 0) {
12785
+ payloads.push({
12786
+ kind: "unpushed_commits",
12787
+ path: "git/unpushed.patch",
12788
+ bytes: Buffer.from(commits)
12789
+ });
12790
+ }
12791
+ }
12792
+ const untracked = splitNul(
12793
+ await git(input.projectDir, [
12794
+ "ls-files",
12795
+ "--others",
12796
+ "--exclude-standard",
12797
+ "-z"
12798
+ ])
12799
+ );
12800
+ for (const rel of untracked) {
12801
+ if (!isAllowedRelativePath(rel, hardDeny)) continue;
12802
+ const bytes = await readWorkspaceFile(input.projectDir, rel);
12803
+ if (!bytes) continue;
12804
+ payloads.push({
12805
+ kind: "untracked_file",
12806
+ path: rel,
12807
+ bytes
12808
+ });
12809
+ }
12810
+ for (const rel of input.includeIgnored ?? []) {
12811
+ if (!isAllowedRelativePath(rel, hardDeny)) continue;
12812
+ if (!await isIgnored(input.projectDir, rel)) continue;
12813
+ const bytes = await readWorkspaceFile(input.projectDir, rel);
12814
+ if (!bytes) continue;
12815
+ payloads.push({
12816
+ kind: "included_ignored_file",
12817
+ path: rel,
12818
+ bytes
12819
+ });
12820
+ }
12821
+ return payloads;
12822
+ }
12823
+ async function readWorkspaceFile(projectDir, rel) {
12824
+ const absolute = path19.resolve(projectDir, rel);
12825
+ const workspaceRoot = await fs21.realpath(projectDir);
12826
+ let stat;
12827
+ try {
12828
+ stat = await fs21.lstat(absolute);
12829
+ } catch {
12830
+ return null;
12831
+ }
12832
+ if (!stat.isFile() || stat.isSymbolicLink()) return null;
12833
+ const real = await fs21.realpath(absolute);
12834
+ const relativeToRoot = path19.relative(workspaceRoot, real);
12835
+ if (relativeToRoot === ".." || relativeToRoot.startsWith(`..${path19.sep}`) || path19.isAbsolute(relativeToRoot)) {
12836
+ return null;
12837
+ }
12838
+ return fs21.readFile(absolute);
12839
+ }
12840
+ async function gitDiffForAllowedPaths(input) {
12841
+ const paths = splitNul(await git(input.projectDir, input.nameArgs)).filter(
12842
+ (rel) => isAllowedRelativePath(rel, input.hardDeny)
12843
+ );
12844
+ if (paths.length === 0) return "";
12845
+ return git(input.projectDir, [...input.diffArgs, ...paths]);
12846
+ }
12847
+ async function uploadGcsObject(input) {
12848
+ const url = new URL(
12849
+ `https://storage.googleapis.com/upload/storage/v1/b/${encodeURIComponent(
12850
+ input.bucket
12851
+ )}/o`
12852
+ );
12853
+ url.searchParams.set("uploadType", "media");
12854
+ url.searchParams.set("name", input.objectName);
12855
+ const response = await fetch(url, {
12856
+ method: "POST",
12857
+ headers: {
12858
+ authorization: `Bearer ${input.accessToken}`,
12859
+ "content-type": input.contentType
12860
+ },
12861
+ body: input.body.buffer.slice(
12862
+ input.body.byteOffset,
12863
+ input.body.byteOffset + input.body.byteLength
12864
+ )
12865
+ });
12866
+ if (!response.ok) {
12867
+ throw new Error(
12868
+ `GCS upload ${input.objectName} failed with HTTP ${response.status}`
12869
+ );
12870
+ }
12871
+ }
12872
+ function makeArtifactManifestFeedEvent(input) {
12873
+ return {
12874
+ event_id: `${input.runId}:artifacts-manifest`,
12875
+ seq: 0,
12876
+ ts: input.ts,
12877
+ session_id: input.result.athenaSessionId ?? input.runId,
12878
+ run_id: input.runId,
12879
+ kind: "artifacts.manifest",
12880
+ level: "info",
12881
+ actor_id: "system",
12882
+ title: "Artifacts manifest",
12883
+ data: { manifest: input.manifest }
12884
+ };
12885
+ }
12886
+ async function git(cwd, args) {
12887
+ const result = await execFileAsync("git", args, {
12888
+ cwd,
12889
+ encoding: "buffer",
12890
+ maxBuffer: 50 * 1024 * 1024
12891
+ });
12892
+ return result.stdout.toString("utf8");
12893
+ }
12894
+ async function gitMaybe(cwd, args) {
12895
+ try {
12896
+ return await git(cwd, args);
12897
+ } catch {
12898
+ return "";
12899
+ }
12900
+ }
12901
+ async function isGitWorkspace(cwd) {
12902
+ return (await gitMaybe(cwd, ["rev-parse", "--is-inside-work-tree"])).trim() === "true";
12903
+ }
12904
+ async function isIgnored(cwd, rel) {
12905
+ try {
12906
+ await execFileAsync("git", ["check-ignore", "--quiet", "--", rel], { cwd });
12907
+ return true;
12908
+ } catch {
12909
+ return false;
12910
+ }
12911
+ }
12912
+ function splitNul(value) {
12913
+ return value.split("\0").filter(Boolean);
12914
+ }
12915
+ function stringArray(value) {
12916
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
12917
+ }
12918
+ var DEFAULT_HARD_DENY = [
12919
+ ".git",
12920
+ ".git/**",
12921
+ ".env",
12922
+ ".env.*",
12923
+ "**/.env",
12924
+ "**/.env.*",
12925
+ ".ssh",
12926
+ ".ssh/**"
12927
+ ];
12928
+ function isAllowedRelativePath(rel, hardDeny) {
12929
+ if (path19.isAbsolute(rel) || rel.includes("\0")) return false;
12930
+ const normalized = path19.posix.normalize(rel.replaceAll(path19.sep, "/"));
12931
+ if (normalized === ".." || normalized.startsWith("../")) return false;
12932
+ return !hardDeny.some((pattern) => matchesDenyPattern(normalized, pattern));
12933
+ }
12934
+ function matchesDenyPattern(rel, pattern) {
12935
+ const normalized = pattern.replaceAll(path19.sep, "/");
12936
+ if (normalized.includes("*") || normalized.includes("?")) {
12937
+ return globToRegExp(normalized).test(rel);
12938
+ }
12939
+ if (normalized.endsWith("/**")) {
12940
+ const prefix = normalized.slice(0, -3);
12941
+ return rel === prefix || rel.startsWith(`${prefix}/`);
12942
+ }
12943
+ if (normalized.startsWith("**/")) {
12944
+ const suffix = normalized.slice(3);
12945
+ return rel === suffix || rel.endsWith(`/${suffix}`);
12946
+ }
12947
+ if (normalized.endsWith(".*")) {
12948
+ const prefix = normalized.slice(0, -1);
12949
+ return rel.startsWith(prefix);
12950
+ }
12951
+ return rel === normalized;
12952
+ }
12953
+ function globToRegExp(pattern) {
12954
+ let source = "^";
12955
+ for (let i = 0; i < pattern.length; i += 1) {
12956
+ const char = pattern[i];
12957
+ const next = pattern[i + 1];
12958
+ if (char === "*") {
12959
+ if (next === "*") {
12960
+ const after = pattern[i + 2];
12961
+ if (after === "/") {
12962
+ source += "(?:.*/)?";
12963
+ i += 2;
12964
+ } else {
12965
+ source += ".*";
12966
+ i += 1;
12967
+ }
12968
+ } else {
12969
+ source += "[^/]*";
12970
+ }
12971
+ } else if (char === "?") {
12972
+ source += "[^/]";
12973
+ } else {
12974
+ source += escapeRegExp(char);
12975
+ }
12976
+ }
12977
+ source += "$";
12978
+ return new RegExp(source);
12979
+ }
12980
+ function escapeRegExp(value) {
12981
+ return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
12982
+ }
12983
+ function sha256(bytes) {
12984
+ return crypto3.createHash("sha256").update(bytes).digest("hex");
12985
+ }
12986
+ function joinObjectName(...parts) {
12987
+ return parts.map((part) => part.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
12988
+ }
12989
+ function safeObjectSegment(value) {
12990
+ const safe = value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
12991
+ return safe.length > 0 ? safe.slice(0, 160) : "artifact";
12992
+ }
12993
+ function contentTypeFor(filePath) {
12994
+ if (filePath.endsWith(".json")) return "application/json";
12995
+ if (filePath.endsWith(".diff") || filePath.endsWith(".patch")) {
12996
+ return "text/x-diff";
12997
+ }
12998
+ return "application/octet-stream";
12999
+ }
13000
+
12589
13001
  // src/app/dashboard/remoteRunExecutor.ts
12590
13002
  var DEFAULT_MARKETPLACE_SLUG = "lespaceman/athena-workflow-marketplace";
12591
13003
  function parseRemoteRunSpec(value) {
@@ -12717,6 +13129,8 @@ async function executeRemoteAssignment({
12717
13129
  resolveWorkflowInstallFn = resolveWorkflowInstall,
12718
13130
  installWorkflowFromSourceFn = installWorkflowFromSource,
12719
13131
  readGlobalConfigFn = readGlobalConfig,
13132
+ uploadArtifactObjectFn,
13133
+ dashboardFeedPublisher,
12720
13134
  runStreamConnectTimeoutMs = 5e3
12721
13135
  }) {
12722
13136
  const lastTerminalFailureMessage = { current: null };
@@ -12746,6 +13160,15 @@ async function executeRemoteAssignment({
12746
13160
  send("error", { message: "remote assignment missing prompt" });
12747
13161
  return;
12748
13162
  }
13163
+ let artifactUploadSpec;
13164
+ try {
13165
+ artifactUploadSpec = parseArtifactUploadSpec(frame.runSpec);
13166
+ } catch (err) {
13167
+ send("error", {
13168
+ message: err instanceof Error ? err.message : String(err)
13169
+ });
13170
+ return;
13171
+ }
12749
13172
  let runtimeConfig;
12750
13173
  try {
12751
13174
  const workflowOverride = ensureRemoteWorkflowInstalled({
@@ -12831,7 +13254,22 @@ async function executeRemoteAssignment({
12831
13254
  signal: abortSignal,
12832
13255
  stdout,
12833
13256
  stderr,
12834
- ...decisionInbox ? { dashboardDecisionInbox: decisionInbox } : {}
13257
+ ...decisionInbox ? { dashboardDecisionInbox: decisionInbox } : {},
13258
+ ...dashboardFeedPublisher ? { dashboardFeedPublisher } : {},
13259
+ ...artifactUploadSpec ? {
13260
+ beforeTerminalCompletion: async ({ result: result2, runId }) => {
13261
+ const artifactRunId = runId ?? frame.runId;
13262
+ const { feedEvent } = await captureAndUploadArtifacts({
13263
+ spec: artifactUploadSpec,
13264
+ projectDir,
13265
+ runId: artifactRunId,
13266
+ result: result2,
13267
+ now,
13268
+ ...uploadArtifactObjectFn ? { uploadObject: uploadArtifactObjectFn } : {}
13269
+ });
13270
+ return [feedEvent];
13271
+ }
13272
+ } : {}
12835
13273
  });
12836
13274
  const failedCompletion = deferredFailedCompletion.current;
12837
13275
  if (failedCompletion) {
@@ -12876,19 +13314,19 @@ async function executeRemoteAssignment({
12876
13314
  }
12877
13315
 
12878
13316
  // src/infra/config/attachmentMirror.ts
12879
- import crypto3 from "crypto";
12880
- import fs21 from "fs";
13317
+ import crypto4 from "crypto";
13318
+ import fs22 from "fs";
12881
13319
  import os12 from "os";
12882
- import path19 from "path";
13320
+ import path20 from "path";
12883
13321
  function attachmentMirrorPath(env = process.env) {
12884
13322
  const home = env["HOME"] ?? os12.homedir();
12885
- return path19.join(home, ".config", "athena", "attachments.json");
13323
+ return path20.join(home, ".config", "athena", "attachments.json");
12886
13324
  }
12887
13325
  function readAttachmentMirror(env = process.env) {
12888
13326
  const file = attachmentMirrorPath(env);
12889
13327
  let raw;
12890
13328
  try {
12891
- raw = fs21.readFileSync(file, "utf-8");
13329
+ raw = fs22.readFileSync(file, "utf-8");
12892
13330
  } catch (err) {
12893
13331
  if (err.code === "ENOENT") return null;
12894
13332
  throw err;
@@ -12912,29 +13350,29 @@ function readAttachmentMirror(env = process.env) {
12912
13350
  function writeAttachmentMirror(mirror, env = process.env) {
12913
13351
  const validated = parseAttachmentMirror(mirror);
12914
13352
  const file = attachmentMirrorPath(env);
12915
- const dir = path19.dirname(file);
12916
- fs21.mkdirSync(dir, { recursive: true, mode: 448 });
12917
- const tmp = `${file}.${process.pid}.${crypto3.randomBytes(4).toString("hex")}.tmp`;
12918
- const fd = fs21.openSync(tmp, "w", 384);
13353
+ const dir = path20.dirname(file);
13354
+ fs22.mkdirSync(dir, { recursive: true, mode: 448 });
13355
+ const tmp = `${file}.${process.pid}.${crypto4.randomBytes(4).toString("hex")}.tmp`;
13356
+ const fd = fs22.openSync(tmp, "w", 384);
12919
13357
  try {
12920
- fs21.writeSync(fd, JSON.stringify(validated, null, 2) + "\n");
12921
- fs21.fsyncSync(fd);
13358
+ fs22.writeSync(fd, JSON.stringify(validated, null, 2) + "\n");
13359
+ fs22.fsyncSync(fd);
12922
13360
  } finally {
12923
- fs21.closeSync(fd);
13361
+ fs22.closeSync(fd);
12924
13362
  }
12925
13363
  try {
12926
- fs21.renameSync(tmp, file);
13364
+ fs22.renameSync(tmp, file);
12927
13365
  } catch (err) {
12928
13366
  try {
12929
- fs21.unlinkSync(tmp);
13367
+ fs22.unlinkSync(tmp);
12930
13368
  } catch {
12931
13369
  }
12932
13370
  throw err;
12933
13371
  }
12934
13372
  if (process.platform !== "win32") {
12935
13373
  try {
12936
- fs21.chmodSync(dir, 448);
12937
- fs21.chmodSync(file, 384);
13374
+ fs22.chmodSync(dir, 448);
13375
+ fs22.chmodSync(file, 384);
12938
13376
  } catch {
12939
13377
  }
12940
13378
  }
@@ -12942,7 +13380,7 @@ function writeAttachmentMirror(mirror, env = process.env) {
12942
13380
  function removeAttachmentMirror(env = process.env) {
12943
13381
  const file = attachmentMirrorPath(env);
12944
13382
  try {
12945
- fs21.unlinkSync(file);
13383
+ fs22.unlinkSync(file);
12946
13384
  } catch (err) {
12947
13385
  if (err.code !== "ENOENT") throw err;
12948
13386
  }
@@ -13208,6 +13646,7 @@ function createDashboardPairedExecution(options) {
13208
13646
  });
13209
13647
  const maxConcurrentRuns = options.maxConcurrentRuns ?? DEFAULT_MAX_CONCURRENT_RUNS;
13210
13648
  const runHistoryLimit = options.runHistoryLimit ?? DEFAULT_RUN_HISTORY_LIMIT;
13649
+ const pairedFeedPublisher = options.pairedFeedPublisher;
13211
13650
  const now = options.now ?? (() => Date.now());
13212
13651
  let completedRuns = 0;
13213
13652
  const active = /* @__PURE__ */ new Map();
@@ -13281,7 +13720,8 @@ function createDashboardPairedExecution(options) {
13281
13720
  projectDir: input.projectDir ?? projectDir,
13282
13721
  log,
13283
13722
  abortSignal: controller.signal,
13284
- decisionInbox
13723
+ decisionInbox,
13724
+ ...pairedFeedPublisher ? { dashboardFeedPublisher: pairedFeedPublisher } : {}
13285
13725
  }).then(() => {
13286
13726
  if (record.status === "running") record.status = "completed";
13287
13727
  }).catch((err) => {
@@ -13352,9 +13792,9 @@ function createDashboardPairedExecution(options) {
13352
13792
  }
13353
13793
 
13354
13794
  // src/app/dashboard/remoteWorkspaceResolver.ts
13355
- import fs22 from "fs";
13795
+ import fs23 from "fs";
13356
13796
  import os13 from "os";
13357
- import path20 from "path";
13797
+ import path21 from "path";
13358
13798
  function resolveRemoteWorkspace(frame, options = {}) {
13359
13799
  const spec = parseRemoteRunSpec(frame.runSpec);
13360
13800
  if (!spec) {
@@ -13373,14 +13813,14 @@ function resolveRemoteWorkspace(frame, options = {}) {
13373
13813
  const runnerId = frame.runnerId ?? "legacy";
13374
13814
  const deploymentSlug = deploymentSlugFromUrl(options.dashboardUrl);
13375
13815
  const stateDir = daemonStatePaths(options.env).dir;
13376
- const projectDir = sessionId ? path20.join(
13816
+ const projectDir = sessionId ? path21.join(
13377
13817
  stateDir,
13378
13818
  "remote-workspaces",
13379
13819
  deploymentSlug,
13380
13820
  sanitizePathSegment(runnerId),
13381
13821
  "sessions",
13382
13822
  sanitizePathSegment(sessionId)
13383
- ) : path20.join(
13823
+ ) : path21.join(
13384
13824
  stateDir,
13385
13825
  "remote-workspaces",
13386
13826
  deploymentSlug,
@@ -13389,10 +13829,10 @@ function resolveRemoteWorkspace(frame, options = {}) {
13389
13829
  sanitizePathSegment(frame.runId)
13390
13830
  );
13391
13831
  try {
13392
- fs22.mkdirSync(projectDir, { recursive: true, mode: 448 });
13832
+ fs23.mkdirSync(projectDir, { recursive: true, mode: 448 });
13393
13833
  if (process.platform !== "win32") {
13394
13834
  try {
13395
- fs22.chmodSync(projectDir, 448);
13835
+ fs23.chmodSync(projectDir, 448);
13396
13836
  } catch {
13397
13837
  }
13398
13838
  }
@@ -13408,8 +13848,8 @@ function resolveRemoteWorkspace(frame, options = {}) {
13408
13848
  return validateProjectDir(projectDir, options.env);
13409
13849
  }
13410
13850
  function validateProjectDir(projectDir, env = process.env) {
13411
- const resolved = path20.resolve(projectDir);
13412
- if (!path20.isAbsolute(projectDir)) {
13851
+ const resolved = path21.resolve(projectDir);
13852
+ if (!path21.isAbsolute(projectDir)) {
13413
13853
  return {
13414
13854
  kind: "rejected",
13415
13855
  rejection: {
@@ -13418,7 +13858,7 @@ function validateProjectDir(projectDir, env = process.env) {
13418
13858
  }
13419
13859
  };
13420
13860
  }
13421
- const home = path20.resolve(env["HOME"] ?? os13.homedir());
13861
+ const home = path21.resolve(env["HOME"] ?? os13.homedir());
13422
13862
  if (resolved === home) {
13423
13863
  return {
13424
13864
  kind: "rejected",
@@ -13430,7 +13870,7 @@ function validateProjectDir(projectDir, env = process.env) {
13430
13870
  }
13431
13871
  let stat;
13432
13872
  try {
13433
- stat = fs22.statSync(resolved);
13873
+ stat = fs23.statSync(resolved);
13434
13874
  } catch {
13435
13875
  return {
13436
13876
  kind: "rejected",
@@ -13612,6 +14052,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
13612
14052
  executor,
13613
14053
  projectDir,
13614
14054
  decisionInbox,
14055
+ pairedFeedPublisher,
13615
14056
  log,
13616
14057
  maxConcurrentRuns,
13617
14058
  now,
@@ -13855,18 +14296,18 @@ async function runDashboardRuntimeDaemon(options = {}) {
13855
14296
  }
13856
14297
 
13857
14298
  // src/infra/daemon/pidLock.ts
13858
- import fs23 from "fs";
14299
+ import fs24 from "fs";
13859
14300
  function acquirePidLock(pidPath) {
13860
14301
  const ownPid = process.pid;
13861
14302
  for (let attempt = 0; attempt < 2; attempt += 1) {
13862
14303
  try {
13863
- const fd = fs23.openSync(pidPath, "wx", 384);
14304
+ const fd = fs24.openSync(pidPath, "wx", 384);
13864
14305
  try {
13865
- fs23.writeSync(fd, `${ownPid}
14306
+ fs24.writeSync(fd, `${ownPid}
13866
14307
  `);
13867
- fs23.fsyncSync(fd);
14308
+ fs24.fsyncSync(fd);
13868
14309
  } finally {
13869
- fs23.closeSync(fd);
14310
+ fs24.closeSync(fd);
13870
14311
  }
13871
14312
  return makeHandle(pidPath, ownPid);
13872
14313
  } catch (err) {
@@ -13880,7 +14321,7 @@ function acquirePidLock(pidPath) {
13880
14321
  }
13881
14322
  if (existing.state === "stale") {
13882
14323
  try {
13883
- fs23.unlinkSync(pidPath);
14324
+ fs24.unlinkSync(pidPath);
13884
14325
  } catch (err) {
13885
14326
  if (err.code !== "ENOENT") throw err;
13886
14327
  }
@@ -13894,7 +14335,7 @@ function acquirePidLock(pidPath) {
13894
14335
  function readPidLock(pidPath) {
13895
14336
  let raw;
13896
14337
  try {
13897
- raw = fs23.readFileSync(pidPath, "utf-8");
14338
+ raw = fs24.readFileSync(pidPath, "utf-8");
13898
14339
  } catch (err) {
13899
14340
  if (err.code === "ENOENT") {
13900
14341
  return { state: "absent" };
@@ -13918,9 +14359,9 @@ function makeHandle(pidPath, pid) {
13918
14359
  if (released) return;
13919
14360
  released = true;
13920
14361
  try {
13921
- const raw = fs23.readFileSync(pidPath, "utf-8").trim();
14362
+ const raw = fs24.readFileSync(pidPath, "utf-8").trim();
13922
14363
  if (raw === String(pid)) {
13923
- fs23.unlinkSync(pidPath);
14364
+ fs24.unlinkSync(pidPath);
13924
14365
  }
13925
14366
  } catch (err) {
13926
14367
  if (err.code !== "ENOENT") {
@@ -13946,7 +14387,7 @@ function isProcessAlive(pid) {
13946
14387
  }
13947
14388
 
13948
14389
  // src/infra/daemon/udsIpc.ts
13949
- import fs24 from "fs";
14390
+ import fs25 from "fs";
13950
14391
  import net2 from "net";
13951
14392
 
13952
14393
  // src/infra/daemon/udsFrameCodec.ts
@@ -13991,7 +14432,7 @@ async function startUdsServer(socketPath, handler, log) {
13991
14432
  });
13992
14433
  if (process.platform !== "win32") {
13993
14434
  try {
13994
- fs24.chmodSync(socketPath, 384);
14435
+ fs25.chmodSync(socketPath, 384);
13995
14436
  } catch {
13996
14437
  }
13997
14438
  }
@@ -14001,7 +14442,7 @@ async function startUdsServer(socketPath, handler, log) {
14001
14442
  server.close(() => resolve());
14002
14443
  });
14003
14444
  try {
14004
- fs24.unlinkSync(socketPath);
14445
+ fs25.unlinkSync(socketPath);
14005
14446
  } catch (err) {
14006
14447
  if (err.code !== "ENOENT") {
14007
14448
  }
@@ -14119,7 +14560,7 @@ async function sendUdsRequest(socketPath, request, options = {}) {
14119
14560
  async function unlinkStaleSocket(socketPath) {
14120
14561
  let stat;
14121
14562
  try {
14122
- stat = fs24.statSync(socketPath);
14563
+ stat = fs25.statSync(socketPath);
14123
14564
  } catch (err) {
14124
14565
  if (err.code === "ENOENT") return;
14125
14566
  throw err;
@@ -14146,7 +14587,7 @@ async function unlinkStaleSocket(socketPath) {
14146
14587
  `uds path ${socketPath} is in use by another process; aborting`
14147
14588
  );
14148
14589
  }
14149
- fs24.unlinkSync(socketPath);
14590
+ fs25.unlinkSync(socketPath);
14150
14591
  }
14151
14592
 
14152
14593
  export {
@@ -14224,4 +14665,4 @@ export {
14224
14665
  startUdsServer,
14225
14666
  sendUdsRequest
14226
14667
  };
14227
- //# sourceMappingURL=chunk-ZU7M6YZW.js.map
14668
+ //# sourceMappingURL=chunk-XIWMD4GT.js.map
package/dist/cli.js CHANGED
@@ -74,7 +74,7 @@ import {
74
74
  writeAttachmentMirror,
75
75
  writeGatewayClientConfig,
76
76
  wsClientOptionsForEndpoint
77
- } from "./chunk-ZU7M6YZW.js";
77
+ } from "./chunk-XIWMD4GT.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.6";
15708
+ const injected = "0.5.8";
15699
15709
  if (typeof injected === "string" && injected.length > 0) {
15700
15710
  cachedVersion = injected;
15701
15711
  return cachedVersion;
@@ -3,7 +3,7 @@ import {
3
3
  ensureDaemonStateDir,
4
4
  runDashboardRuntimeDaemon,
5
5
  startUdsServer
6
- } from "./chunk-ZU7M6YZW.js";
6
+ } from "./chunk-XIWMD4GT.js";
7
7
  import "./chunk-BTKQ67RE.js";
8
8
  import {
9
9
  readDashboardClientConfig,
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "hooks",
18
18
  "dashboard"
19
19
  ],
20
- "version": "0.5.6",
20
+ "version": "0.5.8",
21
21
  "license": "MIT",
22
22
  "bin": {
23
23
  "athena": "dist/cli.js",