replicas-engine 0.1.755 → 0.1.756

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.
@@ -5,11 +5,11 @@ import {
5
5
  getCodexAspHost,
6
6
  restartCodexAspHost,
7
7
  restartCodexAspHostIfRunning
8
- } from "./chunk-JA4PHGL7.js";
9
- import "./chunk-EYRVU2KX.js";
10
- import "./chunk-N6ZBBAGM.js";
8
+ } from "./chunk-QJJE6MBM.js";
9
+ import "./chunk-D7K5M3NA.js";
10
+ import "./chunk-KO62WDEE.js";
11
11
  import "./chunk-UZSNFLDQ.js";
12
- import "./chunk-GQFXO5TG.js";
12
+ import "./chunk-CRFPCWWZ.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
  export {
15
15
  getCodexAspHost,
@@ -3,12 +3,12 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  monolithRequest
6
- } from "./chunk-EYRVU2KX.js";
6
+ } from "./chunk-D7K5M3NA.js";
7
7
  import {
8
8
  extractCommandProtectionCommandText,
9
9
  findGitCommitSignals,
10
10
  findPrMergeSignals
11
- } from "./chunk-GQFXO5TG.js";
11
+ } from "./chunk-CRFPCWWZ.js";
12
12
 
13
13
  // src/services/command-protection-service.ts
14
14
  var DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE = "Blocked by Replicas command protection.";
@@ -2176,18 +2176,24 @@ var GITLAB_ABILITY = {
2176
2176
 
2177
2177
  // ../shared/src/default-skills/replicas-agent/abilities/linear.ts
2178
2178
  var SECTION5 = `### Linear
2179
- Fetch issues, update state, add comments, and search through the workspace-scoped TypeScript SDK.
2179
+ Create tickets, fetch issues, update state, add comments, and search through \`replicas.linear.graphql\` in \`@replicas/sdk\`. This works without a Linear MCP server and refreshes authentication automatically.
2180
2180
 
2181
2181
  **Reference:** \`references/LINEAR.md\`
2182
2182
 
2183
2183
  Use this when:
2184
+ - You need to create a Linear ticket or issue
2184
2185
  - You encounter a Linear issue link and need to understand the task
2185
2186
  - You need to update an issue's state or add a comment
2186
- - You need to search Linear issues`;
2187
+ - You need to search Linear issues
2188
+ - A Linear request failed with an authentication error`;
2187
2189
  var REFERENCE5 = `# Linear Integration
2188
2190
 
2189
2191
  Use \`@replicas/sdk\`; never request or handle a Linear OAuth token. Replicas refreshes the organization credential behind the gateway.
2190
2192
 
2193
+ In gateway-enabled workspaces, Linear credentials stay on the Replicas server and the workspace receives only connection metadata. Older workspaces can still carry a legacy token snapshot, but should use the gateway when the SDK is available. A missing Linear MCP server does not prevent SDK access.
2194
+
2195
+ Download protected attachments with \`await replicas.linear.download(url)\`; this returns a \`Response\` without exposing the provider token.
2196
+
2191
2197
  ## Check access
2192
2198
 
2193
2199
  \`\`\`ts
@@ -2197,6 +2203,25 @@ const { native } = await replicas.integrations.list();
2197
2203
  if (!native.linear) throw new Error('Connect Linear in Replicas organization integrations first.');
2198
2204
  \`\`\`
2199
2205
 
2206
+ \`native.linear\` checks whether an organization credential exists. To diagnose authentication, make a read-only request through \`replicas.linear.graphql({ query: 'query { viewer { id } }' })\`; the gateway refreshes expired credentials and retries authentication failures once.
2207
+
2208
+ Personal \u2192 Connections links a person's Linear identity for agent defaults and attribution. Its disconnected state does not mean the organization's Linear integration is disconnected, and connecting it does not refresh the organization's token. Only recommend reconnecting under Organization \u2192 Integrations when the gateway reports missing credentials or an unrecoverable OAuth failure. Report transient failures without asking the user to disconnect.
2209
+
2210
+ ## Create a ticket
2211
+
2212
+ Fetch \`query { teams { nodes { id name key } } }\` and choose the requested team's ID. Look up IDs for any requested assignee, state, cycle, or labels rather than inventing them.
2213
+
2214
+ \`\`\`ts
2215
+ const created = await replicas.linear.graphql({
2216
+ query: \`mutation($input: IssueCreateInput!) {
2217
+ issueCreate(input: $input) { success issue { id identifier title url } }
2218
+ }\`,
2219
+ variables: { input: { teamId, title, description } },
2220
+ });
2221
+ \`\`\`
2222
+
2223
+ Responses retain Linear's GraphQL \`data\` and \`errors\` fields. Check \`errors\` and \`data.issueCreate.success\` before reporting creation, then return the issue URL. After an ambiguous timeout, search for the issue before retrying creation to avoid duplicates.
2224
+
2200
2225
  ## Fetch an issue
2201
2226
 
2202
2227
  \`\`\`ts
@@ -2230,12 +2255,24 @@ await replicas.linear.graphql({
2230
2255
 
2231
2256
  Search with \`containsIgnoreCase: $query\` filters, for example on both \`title\` and \`description\`. Batch independent queries with \`Promise.all\` to save round trips.
2232
2257
 
2258
+ ## Older workspaces without the SDK
2259
+
2260
+ If \`@replicas/sdk\` cannot be imported, use the same refresh-capable gateway with workspace authentication. Send the GraphQL JSON request on stdin:
2261
+
2262
+ \`\`\`bash
2263
+ curl -sS --fail-with-body -X POST "$REPLICAS_MONOLITH_URL/v1/engine/linear/graphql" \\
2264
+ -H "Authorization: Bearer $REPLICAS_ENGINE_SECRET" \\
2265
+ -H "X-Workspace-Id: $REPLICAS_WORKSPACE_ID" \\
2266
+ -H "Content-Type: application/json" \\
2267
+ --data-binary @-
2268
+ \`\`\`
2269
+
2233
2270
  For screenshots, recordings, diagrams, or other media, read \`references/MEDIA.md\` before adding it to an issue or comment. It is the single source of truth for uploading the Replicas copy and the raw file to Linear; a dashboard link alone is not an inline attachment.
2234
2271
  `;
2235
2272
  var LINEAR_ABILITY = {
2236
2273
  label: "Linear",
2237
- description: "Fetch issues, comment, and update state through @replicas/sdk.",
2238
- bullet: "- Interacting with Linear (fetching issues, updating state, commenting, etc.)",
2274
+ description: "Create tickets, fetch issues, comment, and update state through @replicas/sdk.",
2275
+ bullet: "- Interacting with Linear (creating tickets, fetching issues, updating state, commenting, or troubleshooting authentication): use replicas.linear.graphql from @replicas/sdk, which refreshes tokens automatically and needs no Linear MCP server. Read references/LINEAR.md before treating a rejected workspace token as a disconnected integration.",
2239
2276
  section: SECTION5,
2240
2277
  referenceFile: { name: "LINEAR.md", content: REFERENCE5 }
2241
2278
  };
@@ -2398,36 +2435,25 @@ A Slack message that just contains a \`replicas.dev\` link is not acceptable whe
2398
2435
 
2399
2436
  Slack deprecated the single-shot \`files.upload\` endpoint. The raw HTTP replacement is a 3-step flow: reserve an upload URL with \`files.getUploadURLExternal\`, POST the bytes to it, then share into the channel/thread with \`files.completeUploadExternal\`. (\`files.uploadV2\` is **only** an SDK convenience wrapper \u2014 it is NOT a real Web API HTTP endpoint and \`POST https://slack.com/api/files.uploadV2\` will return \`unknown_method\`.)
2400
2437
 
2401
- \`\`\`bash
2402
- FILE=/abs/path/to/screenshot.png
2403
- FILENAME=$(basename "$FILE")
2404
- LENGTH=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE")
2405
-
2406
- # 1. Reserve an upload URL + file ID.
2407
- RESERVE=$(curl -s -G "https://slack.com/api/files.getUploadURLExternal" \\
2408
- -H "Authorization: Bearer \${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}" \\
2409
- --data-urlencode "filename=$FILENAME" \\
2410
- --data-urlencode "length=$LENGTH")
2411
- UPLOAD_URL=$(echo "$RESERVE" | jq -r '.upload_url')
2412
- FILE_ID=$(echo "$RESERVE" | jq -r '.file_id')
2413
-
2414
- # 2. POST the raw bytes to the reserved URL.
2415
- # Do not use -F / multipart here \u2014 the presigned URL expects the bytes
2416
- # directly in the request body.
2417
- curl -s -X POST "$UPLOAD_URL" \\
2418
- -H "Content-Type: application/octet-stream" \\
2419
- --data-binary @"$FILE"
2438
+ \`\`\`ts
2439
+ import { replicas } from '@replicas/sdk';
2420
2440
 
2421
- # 3. Complete the upload, sharing it into the channel/thread with the dashboard link.
2422
- curl -s -X POST "https://slack.com/api/files.completeUploadExternal" \\
2423
- -H "Authorization: Bearer \${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}" \\
2424
- -H "Content-Type: application/json; charset=utf-8" \\
2425
- -d "{
2426
- \\"files\\": [{\\"id\\": \\"$FILE_ID\\", \\"title\\": \\"Screenshot\\"}],
2427
- \\"channel_id\\": \\"CHANNEL_ID\\",
2428
- \\"thread_ts\\": \\"THREAD_TS\\",
2429
- \\"initial_comment\\": \\"<https://replicas.dev/workspaces/<workspace-id>?media=<media-id>|View in Replicas>\\"
2430
- }"
2441
+ const file = Bun.file('/abs/path/to/screenshot.png');
2442
+ const reservation = await replicas.slack.call<{ upload_url: string; file_id: string }>(
2443
+ 'files.getUploadURLExternal', { filename: 'screenshot.png', length: file.size },
2444
+ );
2445
+ const uploaded = await fetch(reservation.upload_url, {
2446
+ method: 'POST',
2447
+ headers: { 'Content-Type': 'application/octet-stream' },
2448
+ body: file,
2449
+ });
2450
+ if (!uploaded.ok) throw new Error('Slack file upload failed');
2451
+ await replicas.slack.call('files.completeUploadExternal', {
2452
+ files: [{ id: reservation.file_id, title: 'Screenshot' }],
2453
+ channel_id: 'CHANNEL_ID',
2454
+ thread_ts: 'THREAD_TS',
2455
+ initial_comment: '<https://replicas.dev/workspaces/<workspace-id>?media=<media-id>|View in Replicas>',
2456
+ });
2431
2457
  \`\`\`
2432
2458
 
2433
2459
  Notes:
@@ -15,7 +15,7 @@ import {
15
15
  isValidRelayBaseProvider,
16
16
  parsePosixEnvFile,
17
17
  readReplicasRuntimeEnv
18
- } from "./chunk-GQFXO5TG.js";
18
+ } from "./chunk-CRFPCWWZ.js";
19
19
 
20
20
  // src/engine-env.ts
21
21
  import { readFileSync as readFileSync2 } from "fs";
@@ -125,6 +125,8 @@ function loadEngineEnv() {
125
125
  REPLICAS_LINEAR_SESSION_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.linearSessionId),
126
126
  REPLICAS_LINEAR_ACCESS_TOKEN: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.linearAccessToken),
127
127
  REPLICAS_SLACK_BOT_TOKEN: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackBotToken),
128
+ REPLICAS_LINEAR_ACCESS_CONFIGURED: readEnv("REPLICAS_LINEAR_ACCESS_CONFIGURED") === "true",
129
+ REPLICAS_SLACK_ACCESS_CONFIGURED: readEnv("REPLICAS_SLACK_ACCESS_CONFIGURED") === "true",
128
130
  REPLICAS_SLACK_CHANNEL_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackChannelId),
129
131
  REPLICAS_SLACK_THREAD_TS: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackThreadTs),
130
132
  ANTHROPIC_API_KEY: readEnv("ANTHROPIC_API_KEY"),
@@ -3,7 +3,7 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  mayCreatePullRequest
6
- } from "./chunk-GQFXO5TG.js";
6
+ } from "./chunk-CRFPCWWZ.js";
7
7
 
8
8
  // src/services/post-tool-pr-notifier.ts
9
9
  async function notifyPostToolUse(toolName, toolInput) {
@@ -4,7 +4,7 @@ const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  HOOK_EXEC_MAX_BUFFER_BYTES,
6
6
  isVersionBelow
7
- } from "./chunk-GQFXO5TG.js";
7
+ } from "./chunk-CRFPCWWZ.js";
8
8
 
9
9
  // src/utils/codex-agent-env.ts
10
10
  function buildCodexAgentEnv(source = process.env) {
@@ -241,7 +241,7 @@ var DEFAULT_CODEX_ARGS = [
241
241
  var MIN_CODEX_CLI_VERSION = "0.153.3";
242
242
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
243
243
  var codexCliVersionEnsured = null;
244
- var ENGINE_PACKAGE_VERSION = "0.1.755";
244
+ var ENGINE_PACKAGE_VERSION = "0.1.756";
245
245
  var INITIALIZE_METHOD = "initialize";
246
246
  var INITIALIZED_NOTIFICATION = "initialized";
247
247
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -5,18 +5,18 @@ import {
5
5
  ENGINE_ENV,
6
6
  monolithRequest,
7
7
  setAgentCredentialSnapshot
8
- } from "./chunk-EYRVU2KX.js";
8
+ } from "./chunk-D7K5M3NA.js";
9
9
  import {
10
10
  AppServerProcess,
11
11
  buildCodexAgentEnv
12
- } from "./chunk-N6ZBBAGM.js";
12
+ } from "./chunk-KO62WDEE.js";
13
13
  import {
14
14
  CODEX_AUTH_ENV_KEYS,
15
15
  CODEX_AUTH_ENV_KEYS_BY_METHOD,
16
16
  codexAuthEnvFromResponse,
17
17
  createErrorResult,
18
18
  createSuccessResult
19
- } from "./chunk-GQFXO5TG.js";
19
+ } from "./chunk-CRFPCWWZ.js";
20
20
 
21
21
  // src/managers/codex-token-manager.ts
22
22
  import { promises as fs } from "fs";
@@ -232,7 +232,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
232
232
  const data = await response.json();
233
233
  await this.applyCredentialsResponse(data);
234
234
  if (restartOnChange && CODEX_AUTH_ENV_KEYS.some((key, index) => process.env[key] !== previousEnv[index])) {
235
- const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-ORY7LUPI.js");
235
+ const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-RF22WMSU.js");
236
236
  await restartCodexAspHostIfRunning2();
237
237
  }
238
238
  if (data.scope) {
@@ -3,12 +3,12 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  evaluateCommandProtection
6
- } from "./chunk-HJWLVQZ6.js";
7
- import "./chunk-EYRVU2KX.js";
6
+ } from "./chunk-AGAAN5SZ.js";
7
+ import "./chunk-D7K5M3NA.js";
8
8
  import {
9
9
  isRecord
10
10
  } from "./chunk-UZSNFLDQ.js";
11
- import "./chunk-GQFXO5TG.js";
11
+ import "./chunk-CRFPCWWZ.js";
12
12
  import "./chunk-VEQXQN22.js";
13
13
 
14
14
  // src/command-protection-hook.ts
@@ -3,13 +3,13 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  evaluateCommandProtection
6
- } from "./chunk-HJWLVQZ6.js";
6
+ } from "./chunk-AGAAN5SZ.js";
7
7
  import {
8
8
  notifyPostToolUse
9
- } from "./chunk-LAK7IDRF.js";
10
- import "./chunk-EYRVU2KX.js";
9
+ } from "./chunk-KERIK6MD.js";
10
+ import "./chunk-D7K5M3NA.js";
11
11
  import "./chunk-UZSNFLDQ.js";
12
- import "./chunk-GQFXO5TG.js";
12
+ import "./chunk-CRFPCWWZ.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/deepseek-command-protection-plugin.ts
@@ -8,12 +8,12 @@ import {
8
8
  import {
9
9
  AppServerProcess,
10
10
  buildCodexAgentEnv
11
- } from "./chunk-N6ZBBAGM.js";
11
+ } from "./chunk-KO62WDEE.js";
12
12
  import {
13
13
  AGENT,
14
14
  getMemoryOutputSafetyViolation,
15
15
  headlessAgentRequestSchema
16
- } from "./chunk-GQFXO5TG.js";
16
+ } from "./chunk-CRFPCWWZ.js";
17
17
  import "./chunk-VEQXQN22.js";
18
18
 
19
19
  // src/headless-agent.ts
package/dist/src/index.js CHANGED
@@ -91,7 +91,7 @@ import {
91
91
  evaluateCommandProtection,
92
92
  extractToolCommand,
93
93
  reportCommandProtectionBlock
94
- } from "./chunk-HJWLVQZ6.js";
94
+ } from "./chunk-AGAAN5SZ.js";
95
95
  import {
96
96
  ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
97
97
  AGENT_MESSAGE_DELTA_METHOD,
@@ -124,20 +124,20 @@ import {
124
124
  recordCredentialFallback,
125
125
  recordExhaustedCredential,
126
126
  restartCodexAspHost
127
- } from "./chunk-JA4PHGL7.js";
127
+ } from "./chunk-QJJE6MBM.js";
128
128
  import {
129
129
  ENGINE_ENV,
130
130
  IS_WARMING_MODE,
131
131
  monolithRequest,
132
132
  monolithService,
133
133
  setAgentCredentialSnapshot
134
- } from "./chunk-EYRVU2KX.js";
134
+ } from "./chunk-D7K5M3NA.js";
135
135
  import {
136
136
  AspClient,
137
137
  SUBPROCESS_MAX_BUFFER,
138
138
  execAsync,
139
139
  execFileAsync
140
- } from "./chunk-N6ZBBAGM.js";
140
+ } from "./chunk-KO62WDEE.js";
141
141
  import {
142
142
  isRecord as isRecord2
143
143
  } from "./chunk-UZSNFLDQ.js";
@@ -330,7 +330,7 @@ import {
330
330
  spawnRelaySubagentRequestSchema,
331
331
  stripAgentDiagnosticErrors,
332
332
  withTimeout
333
- } from "./chunk-GQFXO5TG.js";
333
+ } from "./chunk-CRFPCWWZ.js";
334
334
  import {
335
335
  __commonJS,
336
336
  __export,
@@ -9890,9 +9890,11 @@ var EnvironmentDetailsService = class {
9890
9890
  details.gitlabAccessConfigured = gitlabAccessConfigured;
9891
9891
  details.gitlabCredentialsConfigured = gitlabAccessConfigured;
9892
9892
  details.linearAccessConfigured = Boolean(
9893
- ENGINE_ENV.REPLICAS_LINEAR_SESSION_ID || ENGINE_ENV.REPLICAS_LINEAR_ACCESS_TOKEN
9893
+ ENGINE_ENV.REPLICAS_LINEAR_SESSION_ID || ENGINE_ENV.REPLICAS_LINEAR_ACCESS_TOKEN || ENGINE_ENV.REPLICAS_LINEAR_ACCESS_CONFIGURED
9894
+ );
9895
+ details.slackAccessConfigured = Boolean(
9896
+ ENGINE_ENV.REPLICAS_SLACK_BOT_TOKEN || ENGINE_ENV.REPLICAS_SLACK_ACCESS_CONFIGURED
9894
9897
  );
9895
- details.slackAccessConfigured = Boolean(ENGINE_ENV.REPLICAS_SLACK_BOT_TOKEN);
9896
9898
  const freshRepos = repositories.map((repo) => ({
9897
9899
  repositoryName: repo.name,
9898
9900
  warmHookCompleted: "n/a",
@@ -11211,39 +11213,10 @@ function inferMediaType(url3, contentType) {
11211
11213
  if (urlLower.includes(".webp")) return "image/webp";
11212
11214
  return "image/jpeg";
11213
11215
  }
11214
- async function getLinearAccessToken() {
11215
- try {
11216
- const response = await monolithRequest("/v1/engine/linear/refresh-token");
11217
- if (!response.ok) {
11218
- console.warn(`[ImageUtils] Failed to refresh Linear token: ${response.status} ${await response.text()}`);
11219
- return ENGINE_ENV.REPLICAS_LINEAR_ACCESS_TOKEN;
11220
- }
11221
- const data = await response.json();
11222
- return data.token;
11223
- } catch (error) {
11224
- console.warn("[ImageUtils] Failed to refresh Linear token:", error);
11225
- return ENGINE_ENV.REPLICAS_LINEAR_ACCESS_TOKEN;
11226
- }
11227
- }
11228
- function isLinearUploadUrl(url3) {
11229
- return new URL(url3).hostname === "uploads.linear.app";
11230
- }
11231
- async function fetchImageAsBase64(url3, linearAccessToken) {
11232
- const headers = {};
11216
+ async function fetchImageAsBase64(url3) {
11233
11217
  const hostname = new URL(url3).hostname;
11234
- if (hostname === "uploads.linear.app") {
11235
- if (linearAccessToken) {
11236
- headers["Authorization"] = `Bearer ${linearAccessToken}`;
11237
- }
11238
- }
11239
- if (hostname === "files.slack.com") {
11240
- const token = ENGINE_ENV.REPLICAS_SLACK_BOT_TOKEN;
11241
- if (!token) {
11242
- throw new Error(`Cannot fetch Slack file from ${url3}: REPLICAS_SLACK_BOT_TOKEN is not configured`);
11243
- }
11244
- headers["Authorization"] = `Bearer ${token}`;
11245
- }
11246
- const response = await fetch(url3, { headers, redirect: "follow" });
11218
+ const provider = hostname === "uploads.linear.app" ? "linear" : hostname === "files.slack.com" ? "slack" : null;
11219
+ const response = provider ? await monolithRequest(`/v1/engine/${provider}/download`, { body: { url: url3 } }) : await fetch(url3, { redirect: "follow" });
11247
11220
  if (!response.ok) {
11248
11221
  throw new Error(`Failed to fetch image from ${url3}: ${response.status} ${response.statusText}`);
11249
11222
  }
@@ -11268,8 +11241,6 @@ async function fetchImageAsBase64(url3, linearAccessToken) {
11268
11241
  }
11269
11242
  async function normalizeImages(images) {
11270
11243
  const normalized = [];
11271
- let linearAccessToken;
11272
- let linearAccessTokenLoaded = false;
11273
11244
  for (const image of images) {
11274
11245
  if (image.source.type === "base64") {
11275
11246
  normalized.push({
@@ -11277,11 +11248,7 @@ async function normalizeImages(images) {
11277
11248
  source: image.source
11278
11249
  });
11279
11250
  } else if (image.source.type === "url") {
11280
- if (!linearAccessTokenLoaded && isLinearUploadUrl(image.source.url)) {
11281
- linearAccessToken = await getLinearAccessToken();
11282
- linearAccessTokenLoaded = true;
11283
- }
11284
- const base64Source = await fetchImageAsBase64(image.source.url, linearAccessToken);
11251
+ const base64Source = await fetchImageAsBase64(image.source.url);
11285
11252
  normalized.push({
11286
11253
  type: "image",
11287
11254
  source: base64Source
@@ -3,11 +3,11 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  notifyPostToolUse
6
- } from "./chunk-LAK7IDRF.js";
6
+ } from "./chunk-KERIK6MD.js";
7
7
  import {
8
8
  isRecord
9
9
  } from "./chunk-UZSNFLDQ.js";
10
- import "./chunk-GQFXO5TG.js";
10
+ import "./chunk-CRFPCWWZ.js";
11
11
  import "./chunk-VEQXQN22.js";
12
12
 
13
13
  // src/post-tool-pr-hook.ts
@@ -4,7 +4,7 @@ const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  messageRelaySubagentRequestSchema,
6
6
  spawnRelaySubagentRequestSchema
7
- } from "./chunk-GQFXO5TG.js";
7
+ } from "./chunk-CRFPCWWZ.js";
8
8
  import "./chunk-VEQXQN22.js";
9
9
 
10
10
  // src/relay-mcp.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.755",
3
+ "version": "0.1.756",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -79,6 +79,10 @@ const plugins = {
79
79
  };
80
80
 
81
81
  const linear = {
82
+ download: (url) => requestResponse('/v1/engine/linear/download', {
83
+ method: 'POST',
84
+ body: { url },
85
+ }),
82
86
  graphql: ({ query, variables, operationName }) =>
83
87
  request('/v1/engine/linear/graphql', {
84
88
  method: 'POST',
@@ -57,7 +57,10 @@ describe('@replicas/sdk', () => {
57
57
  await expect(replicas.plugins.list()).rejects.toThrow('missing Replicas workspace credentials');
58
58
  });
59
59
 
60
- test('downloads Slack files through the authenticated workspace gateway', async () => {
60
+ test.each([
61
+ ['slack', 'https://files.slack.com/files-pri/example.mov'],
62
+ ['linear', 'https://uploads.linear.app/example.mov'],
63
+ ] as const)('downloads %s files through the authenticated workspace gateway', async (provider, url) => {
61
64
  let observed: { path: string; authorization: string | null; body: unknown } | null = null;
62
65
  const server = Bun.serve({
63
66
  port: 0,
@@ -74,11 +77,11 @@ describe('@replicas/sdk', () => {
74
77
  process.env.REPLICAS_ENGINE_SECRET = 'engine-secret';
75
78
  process.env.REPLICAS_WORKSPACE_ID = 'workspace-1';
76
79
  try {
77
- const response = await replicas.slack.download('https://files.slack.com/files-pri/example.mov');
80
+ const response = await replicas[provider].download(url);
78
81
  expect(observed).toEqual({
79
- path: '/v1/engine/slack/download',
82
+ path: `/v1/engine/${provider}/download`,
80
83
  authorization: 'Bearer engine-secret',
81
- body: { url: 'https://files.slack.com/files-pri/example.mov' },
84
+ body: { url },
82
85
  });
83
86
  expect(response.headers.get('content-type')).toBe('video/quicktime');
84
87
  expect(await response.text()).toBe('video-bytes');
@@ -18,6 +18,7 @@ export interface ReplicasSdk {
18
18
  }>;
19
19
  };
20
20
  linear: {
21
+ download(url: string): Promise<Response>;
21
22
  graphql<T = unknown>(input: {
22
23
  query: string;
23
24
  variables?: Record<string, unknown> | null;