@oqtopus-team/pi-qdash 0.1.10-beta.13 → 0.1.10-beta.15

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.
@@ -9,7 +9,17 @@ function firstString(object: Record<string, unknown>, keys: string[]): string |
9
9
  return undefined;
10
10
  }
11
11
 
12
- export function qdashWebBaseUrl(client: QDashClient): string {
12
+ /**
13
+ * Where the QDash web UI lives, for links in tool output.
14
+ *
15
+ * `QDASH_WEB_URL` wins when set: inside a deployment the API base URL is an
16
+ * internal address (`http://api:5715`) that no browser can open. Without it,
17
+ * the UI is assumed to sit next to the API with the `/api` prefix stripped,
18
+ * which holds for a local checkout.
19
+ */
20
+ export function qdashWebBaseUrl(client: QDashClient, env: NodeJS.ProcessEnv = process.env): string {
21
+ const configured = env.QDASH_WEB_URL?.trim();
22
+ if (configured) return configured.replace(/\/+$/, "");
13
23
  return client.config.baseUrl.replace(/\/api\/?$/, "").replace(/\/$/, "");
14
24
  }
15
25
 
@@ -17,16 +27,28 @@ export function qdashWebUrl(client: QDashClient, path: string): string {
17
27
  return `${qdashWebBaseUrl(client)}${path.startsWith("/") ? path : `/${path}`}`;
18
28
  }
19
29
 
30
+ /** Path of an execution page: under its chip when known, else the id-only route that redirects. */
31
+ export function executionPagePath(executionId: string, chipId?: string | null): string {
32
+ return chipId
33
+ ? `/execution/${encodeURIComponent(chipId)}/${encodeURIComponent(executionId)}`
34
+ : `/executions/${encodeURIComponent(executionId)}`;
35
+ }
36
+
37
+ export function forumPostPagePath(postId: string): string {
38
+ return `/forum/${encodeURIComponent(postId)}`;
39
+ }
40
+
20
41
  export function qdashObjectLinks(client: QDashClient, object: Record<string, unknown>): Record<string, string> {
21
42
  const links: Record<string, string> = {};
22
43
  const taskId = firstString(object, ["task_id", "taskId"]);
23
44
  const executionId = firstString(object, ["execution_id", "executionId"]);
45
+ const chipId = firstString(object, ["chip_id", "chipId"]);
24
46
  const postId = firstString(object, ["post_id", "forum_post_id", "id"]);
25
47
  const issueId = firstString(object, ["issue_id"]);
26
48
  const sessionId = firstString(object, ["session_id", "sessionId"]);
27
49
  if (taskId) links.task_result = qdashWebUrl(client, `/task-results/${encodeURIComponent(taskId)}`);
28
- if (executionId) links.execution = qdashWebUrl(client, `/executions/${encodeURIComponent(executionId)}`);
29
- if (postId) links.forum_post = qdashWebUrl(client, `/forum/posts/${encodeURIComponent(postId)}`);
50
+ if (executionId) links.execution = qdashWebUrl(client, executionPagePath(executionId, chipId));
51
+ if (postId) links.forum_post = qdashWebUrl(client, forumPostPagePath(postId));
30
52
  if (issueId) links.issue = qdashWebUrl(client, `/issues/${encodeURIComponent(issueId)}`);
31
53
  if (sessionId) links.agent_session = qdashWebUrl(client, `/agent-sessions/${encodeURIComponent(sessionId)}`);
32
54
  return links;
@@ -19,7 +19,7 @@ import { fetchFigureDetails, figureComponent, figureResultText, mediaTypeForPath
19
19
  import { forumImageReplyContent, forumImageReplyPreview, loadLocalForumImages, type LocalForumImage } from "./lib/forum-images.js";
20
20
  import { forumDetailComponent, forumDetailLines, forumListLines, forumPostTitle, forumPostsFromPayload } from "./lib/forum-render.js";
21
21
  import { investigationContextSummary, validateInvestigationContext, type TimeseriesInvestigationContext } from "./lib/investigation-context.js";
22
- import { qdashObjectLinks, qdashWebBaseUrl, qdashWebUrl, safeConfig, withQDashLinks } from "./lib/links.js";
22
+ import { executionPagePath, forumPostPagePath, qdashObjectLinks, qdashWebBaseUrl, qdashWebUrl, safeConfig, withQDashLinks } from "./lib/links.js";
23
23
  import { arrayFromPayload, compactItems, firstNumber, firstString, formatItem, payloadTotal, statusIcon } from "./lib/payload.js";
24
24
  import { toFigureToolResult, toTextToolResult, toToolResult } from "./lib/results.js";
25
25
  import { ansi, boxLinesToWidth, boxed, formatNumber, textComponent } from "./lib/render.js";
@@ -1419,7 +1419,7 @@ async function buildForumEvidenceReply(client: QDashClient, params: ForumEvidenc
1419
1419
  const targetId = firstString(parent, ["target_id", "targetId"]) ?? couplingId ?? qid;
1420
1420
  const title = params.title ?? `${new Date().toISOString().slice(0, 10)} 追加観測: ${target} ${taskName}`;
1421
1421
  const taskUrl = qdashWebUrl(client, `/task-results/${encodeURIComponent(params.taskId)}`);
1422
- const executionUrl = executionId ? qdashWebUrl(client, `/executions/${encodeURIComponent(executionId)}`) : undefined;
1422
+ const executionUrl = executionId ? qdashWebUrl(client, executionPagePath(executionId, chipId)) : undefined;
1423
1423
  const figures = params.includeFigures === false ? [] : taskFigurePaths(task, params.maxFigures ?? 2);
1424
1424
  const figureMarkdown = figures.map((path) => `![${filenameFromFigurePath(path)}](${figureApiUrl(path)})`).join("\n\n");
1425
1425
  const history = params.includeHistory === false ? [] : arrayFromPayload(await client.listTaskResults({
@@ -2238,7 +2238,7 @@ export default function qdashExtension(pi: ExtensionAPI) {
2238
2238
  async execute(_toolCallId, params: { profile?: string; configPath?: string; useEnv?: boolean; postId: string; color?: boolean }) {
2239
2239
  const client = await makeClient(params);
2240
2240
  const data = await client.getForumPost(params.postId);
2241
- const url = qdashWebUrl(client, `/forum/posts/${encodeURIComponent(params.postId)}`);
2241
+ const url = qdashWebUrl(client, forumPostPagePath(params.postId));
2242
2242
  return toTextToolResult(`${forumDetailLines(data, "QDash Forum Post", params.color).join("\n")}\nurl ${url}`, withQDashLinks(client, data), { tool: "qdash_get_forum_post", url });
2243
2243
  },
2244
2244
  renderResult(result, _options, theme) {
@@ -2257,7 +2257,7 @@ export default function qdashExtension(pi: ExtensionAPI) {
2257
2257
  async execute(_toolCallId, params: { profile?: string; configPath?: string; useEnv?: boolean; postId: string; color?: boolean }) {
2258
2258
  const client = await makeClient(params);
2259
2259
  const data = await client.getForumPostReplies(params.postId);
2260
- const url = qdashWebUrl(client, `/forum/posts/${encodeURIComponent(params.postId)}`);
2260
+ const url = qdashWebUrl(client, forumPostPagePath(params.postId));
2261
2261
  return toTextToolResult(`${forumListLines(data, "QDash Forum Replies", params.color).join("\n")}\nurl ${url}`, data, { tool: "qdash_list_forum_replies", url });
2262
2262
  },
2263
2263
  renderResult(result, _options, theme) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oqtopus-team/pi-qdash",
3
- "version": "0.1.10-beta.13",
3
+ "version": "0.1.10-beta.15",
4
4
  "description": "Pi extension for querying QDash via @oqtopus-team/qdash-client",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -134,6 +134,7 @@ For cryostat wiring requests, start with `qdash_get_cooldown_wiring`. Pass `cool
134
134
  The extension uses `@oqtopus-team/qdash-client` and supports:
135
135
 
136
136
  - `QDASH_*` environment variables (`QDASH_BASE_URL`, `QDASH_API_TOKEN`, `QDASH_PROJECT_ID`, etc.)
137
+ - `QDASH_WEB_URL`: the QDash web UI address used for links in tool output, when it differs from the API base URL (for example inside a deployment where the API is reached by an internal hostname)
137
138
  - `$XDG_CONFIG_HOME/qdash/config.ini`
138
139
  - `~/.config/qdash/config.ini`
139
140
  - explicit `profile` / `configPath` parameters