alink-cli 0.12.6 → 0.12.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.
package/dist/bin.mjs CHANGED
@@ -13862,25 +13862,39 @@ const ProviderApprovalDecision = Literals([
13862
13862
  ]);
13863
13863
  const ProviderUserInputAnswers = Record(String$1, Unknown);
13864
13864
  const PROVIDER_SEND_TURN_MAX_INPUT_CHARS = 12e4;
13865
- const PROVIDER_SEND_TURN_MAX_IMAGE_BYTES = 10 * 1024 * 1024;
13866
- const PROVIDER_SEND_TURN_MAX_IMAGE_DATA_URL_CHARS = 14e6;
13865
+ const PROVIDER_SEND_TURN_MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;
13866
+ const PROVIDER_SEND_TURN_MAX_ATTACHMENT_DATA_URL_CHARS = 14e6;
13867
13867
  const ChatAttachmentId = TrimmedNonEmptyString.check(isMaxLength(128), isPattern(/^[a-z0-9_-]+$/i));
13868
13868
  const ChatImageAttachment = Struct({
13869
13869
  type: Literal("image"),
13870
13870
  id: ChatAttachmentId,
13871
13871
  name: TrimmedNonEmptyString.check(isMaxLength(255)),
13872
13872
  mimeType: TrimmedNonEmptyString.check(isMaxLength(100), isPattern(/^image\//i)),
13873
- sizeBytes: NonNegativeInt.check(isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_IMAGE_BYTES))
13873
+ sizeBytes: NonNegativeInt.check(isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_ATTACHMENT_BYTES))
13874
13874
  });
13875
13875
  const UploadChatImageAttachment = Struct({
13876
13876
  type: Literal("image"),
13877
13877
  name: TrimmedNonEmptyString.check(isMaxLength(255)),
13878
13878
  mimeType: TrimmedNonEmptyString.check(isMaxLength(100), isPattern(/^image\//i)),
13879
- sizeBytes: NonNegativeInt.check(isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_IMAGE_BYTES)),
13880
- dataUrl: TrimmedNonEmptyString.check(isMaxLength(PROVIDER_SEND_TURN_MAX_IMAGE_DATA_URL_CHARS))
13879
+ sizeBytes: NonNegativeInt.check(isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_ATTACHMENT_BYTES)),
13880
+ dataUrl: TrimmedNonEmptyString.check(isMaxLength(PROVIDER_SEND_TURN_MAX_ATTACHMENT_DATA_URL_CHARS))
13881
13881
  });
13882
- const ChatAttachment = Union([ChatImageAttachment]);
13883
- const UploadChatAttachment = Union([UploadChatImageAttachment]);
13882
+ const ChatFileAttachment = Struct({
13883
+ type: Literal("file"),
13884
+ id: ChatAttachmentId,
13885
+ name: TrimmedNonEmptyString.check(isMaxLength(255)),
13886
+ mimeType: TrimmedNonEmptyString.check(isMaxLength(100)),
13887
+ sizeBytes: NonNegativeInt.check(isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_ATTACHMENT_BYTES))
13888
+ });
13889
+ const UploadChatFileAttachment = Struct({
13890
+ type: Literal("file"),
13891
+ name: TrimmedNonEmptyString.check(isMaxLength(255)),
13892
+ mimeType: TrimmedNonEmptyString.check(isMaxLength(100)),
13893
+ sizeBytes: NonNegativeInt.check(isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_ATTACHMENT_BYTES)),
13894
+ dataUrl: TrimmedNonEmptyString.check(isMaxLength(PROVIDER_SEND_TURN_MAX_ATTACHMENT_DATA_URL_CHARS))
13895
+ });
13896
+ const ChatAttachment = Union([ChatImageAttachment, ChatFileAttachment]);
13897
+ const UploadChatAttachment = Union([UploadChatImageAttachment, UploadChatFileAttachment]);
13884
13898
  const ProjectScriptIcon = Literals([
13885
13899
  "play",
13886
13900
  "test",
@@ -22491,7 +22505,7 @@ var AssetWorkspacePathValidationError = class extends TaggedErrorClass()("AssetW
22491
22505
  };
22492
22506
  var AssetPreviewTypeValidationError = class extends TaggedErrorClass()("AssetPreviewTypeValidationError", { resource: AssetResource }) {
22493
22507
  get message() {
22494
- return "Only browser documents and images can be previewed.";
22508
+ return "Only browser documents, media, and images can be previewed.";
22495
22509
  }
22496
22510
  };
22497
22511
  var AssetWorkspaceAssetInspectionError = class extends TaggedErrorClass()("AssetWorkspaceAssetInspectionError", {
@@ -37795,8 +37809,30 @@ function attachmentRelativePath(attachment) {
37795
37809
  });
37796
37810
  return `${attachment.id}${extension}`;
37797
37811
  }
37812
+ case "file": return `${attachment.id}.bin`;
37798
37813
  }
37799
37814
  }
37815
+ function prepareProviderAttachmentInput(input) {
37816
+ const imageAttachments = [];
37817
+ const fileLines = [];
37818
+ for (const attachment of input.attachments) {
37819
+ if (attachment.type === "image") {
37820
+ imageAttachments.push(attachment);
37821
+ continue;
37822
+ }
37823
+ const filePath = resolveAttachmentPath({
37824
+ attachmentsDir: input.attachmentsDir,
37825
+ attachment
37826
+ });
37827
+ if (!filePath || !NodeFS.existsSync(filePath)) return null;
37828
+ fileLines.push(`- ${JSON.stringify(attachment.name)}: ${filePath}`);
37829
+ }
37830
+ const text = input.text?.trim();
37831
+ return {
37832
+ text: fileLines.length === 0 ? text : [text, ["Attached files (local paths):", ...fileLines].join("\n")].filter(Boolean).join("\n\n"),
37833
+ imageAttachments
37834
+ };
37835
+ }
37800
37836
  function resolveAttachmentPath(input) {
37801
37837
  return resolveAttachmentRelativePath({
37802
37838
  attachmentsDir: input.attachmentsDir,
@@ -37939,7 +37975,6 @@ function collectThreadAttachmentRelativePaths(threadId, messages) {
37939
37975
  if (!threadSegment) return /* @__PURE__ */ new Set();
37940
37976
  const relativePaths = /* @__PURE__ */ new Set();
37941
37977
  for (const message of messages) for (const attachment of message.attachments ?? []) {
37942
- if (attachment.type !== "image") continue;
37943
37978
  const attachmentThreadSegment = parseThreadSegmentFromAttachmentId(attachment.id);
37944
37979
  if (!attachmentThreadSegment || attachmentThreadSegment !== threadSegment) continue;
37945
37980
  relativePaths.add(attachmentRelativePath(attachment));
@@ -45657,11 +45692,6 @@ function parseBigInt(input) {
45657
45692
  }
45658
45693
  //#endregion
45659
45694
  //#region ../t3-shared/src/filePreview.ts
45660
- const WORKSPACE_BROWSER_PREVIEW_EXTENSIONS = [
45661
- ".htm",
45662
- ".html",
45663
- ".pdf"
45664
- ];
45665
45695
  const WORKSPACE_IMAGE_PREVIEW_EXTENSIONS = [
45666
45696
  ".avif",
45667
45697
  ".gif",
@@ -45676,15 +45706,9 @@ function hasPreviewExtension(path, extensions) {
45676
45706
  const pathWithoutQuery = path.split(/[?#]/, 1)[0]?.toLowerCase() ?? "";
45677
45707
  return extensions.some((extension) => pathWithoutQuery.endsWith(extension));
45678
45708
  }
45679
- function isWorkspaceBrowserPreviewPath(path) {
45680
- return hasPreviewExtension(path, WORKSPACE_BROWSER_PREVIEW_EXTENSIONS);
45681
- }
45682
45709
  function isWorkspaceImagePreviewPath(path) {
45683
45710
  return hasPreviewExtension(path, WORKSPACE_IMAGE_PREVIEW_EXTENSIONS);
45684
45711
  }
45685
- function isWorkspacePreviewEntryPath(path) {
45686
- return isWorkspaceBrowserPreviewPath(path) || isWorkspaceImagePreviewPath(path);
45687
- }
45688
45712
  //#endregion
45689
45713
  //#region ../t3-shared/src/projectFavicon.ts
45690
45714
  const PROJECT_FAVICON_FALLBACK_MARKER = "project-favicon-missing";
@@ -45886,17 +45910,6 @@ const SIGNING_SECRET_NAME = "asset-access-signing-key";
45886
45910
  const ASSET_TOKEN_TTL_MS = 3600 * 1e3;
45887
45911
  const PROJECT_FAVICON_TOKEN_BUCKET_MS = 1800 * 1e3;
45888
45912
  const PROJECT_FAVICON_VERSION_PREFIX = "v";
45889
- const PREVIEW_ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
45890
- ...WORKSPACE_BROWSER_PREVIEW_EXTENSIONS,
45891
- ...WORKSPACE_IMAGE_PREVIEW_EXTENSIONS,
45892
- ".css",
45893
- ".js",
45894
- ".mjs",
45895
- ".otf",
45896
- ".ttf",
45897
- ".woff",
45898
- ".woff2"
45899
- ]);
45900
45913
  const AssetClaimsJson = fromJsonString(Union([
45901
45914
  Struct({
45902
45915
  version: Literal(1),
@@ -45992,7 +46005,6 @@ const issueAssetUrl = fn("AssetAccess.issueAssetUrl")(function* (input) {
45992
46005
  resource: input.resource,
45993
46006
  cause
45994
46007
  })));
45995
- if (!isWorkspacePreviewEntryPath(resolved.relativePath)) return yield* new AssetPreviewTypeValidationError({ resource: input.resource });
45996
46008
  if (!(yield* resolveCanonicalWorkspaceFile({
45997
46009
  workspaceRoot,
45998
46010
  relativePath: resolved.relativePath
@@ -46148,7 +46160,7 @@ const resolveAsset = fn("AssetAccess.resolveAsset")(function* (token, relativePa
46148
46160
  } : null;
46149
46161
  }
46150
46162
  const segments = decodedPath.split(/[\\/]/);
46151
- if (decodedPath.length === 0 || decodedPath.includes("\0") || segments.some((segment) => segment === "." || segment === ".." || segment.startsWith(".")) || !PREVIEW_ASSET_EXTENSIONS.has(path.extname(decodedPath).toLowerCase())) return null;
46163
+ if (decodedPath.length === 0 || decodedPath.includes("\0") || segments.some((segment) => segment === "." || segment === ".." || segment.startsWith("."))) return null;
46152
46164
  const joinedRelativePath = claims.baseRelativePath === "." ? decodedPath : path.join(claims.baseRelativePath, decodedPath);
46153
46165
  const workspaceFile = yield* resolveCanonicalWorkspaceFileForRequest({
46154
46166
  workspaceRoot: claims.workspaceRoot,
@@ -51178,13 +51190,13 @@ const normalizeDispatchCommand = (command) => gen(function* () {
51178
51190
  if (canonicalCommand.type !== "thread.turn.start") return canonicalCommand;
51179
51191
  const normalizedAttachments = yield* forEach(canonicalCommand.message.attachments, (attachment) => gen(function* () {
51180
51192
  const parsed = parseBase64DataUrl(attachment.dataUrl);
51181
- if (!parsed || !parsed.mimeType.startsWith("image/")) return yield* new OrchestrationDispatchCommandError({ message: `Invalid image attachment payload for '${attachment.name}'.` });
51193
+ if (!parsed || attachment.type === "image" && !parsed.mimeType.startsWith("image/")) return yield* new OrchestrationDispatchCommandError({ message: `Invalid attachment payload for '${attachment.name}'.` });
51182
51194
  const bytes = Buffer.from(parsed.base64, "base64");
51183
- if (bytes.byteLength === 0 || bytes.byteLength > 10485760) return yield* new OrchestrationDispatchCommandError({ message: `Image attachment '${attachment.name}' is empty or too large.` });
51195
+ if (bytes.byteLength === 0 || bytes.byteLength > 10485760) return yield* new OrchestrationDispatchCommandError({ message: `Attachment '${attachment.name}' is empty or too large.` });
51184
51196
  const attachmentId = createAttachmentId(canonicalCommand.threadId);
51185
51197
  if (!attachmentId) return yield* new OrchestrationDispatchCommandError({ message: "Failed to create a safe attachment id." });
51186
51198
  const persistedAttachment = {
51187
- type: "image",
51199
+ type: attachment.type,
51188
51200
  id: attachmentId,
51189
51201
  name: attachment.name,
51190
51202
  mimeType: parsed.mimeType.toLowerCase(),
@@ -51353,12 +51365,12 @@ function normalizeSearchQuery(input, options) {
51353
51365
  //#endregion
51354
51366
  //#region src/workspace/WorkspaceSearchIndex.ts
51355
51367
  const WORKSPACE_INDEX_MAX_ENTRIES = 25e3;
51356
- const WORKSPACE_INDEX_PAGE_SIZE = 25002;
51357
51368
  const WORKSPACE_INDEX_SCAN_TIMEOUT = "15 seconds";
51358
51369
  const WORKSPACE_INDEX_SCAN_TIMEOUT_MS = 15e3;
51359
51370
  const WORKSPACE_INDEX_IDLE_TTL = "15 minutes";
51360
51371
  const CONTENT_SEARCH_TIME_BUDGET_MS = 250;
51361
51372
  const CONTENT_SEARCH_MAX_MATCHES_PER_FILE = 100;
51373
+ const WORKSPACE_LIST_SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
51362
51374
  var WorkspaceSearchIndexCreateFailed = class extends TaggedErrorClass()("WorkspaceSearchIndexCreateFailed", {
51363
51375
  cwd: String$1,
51364
51376
  reason: String$1,
@@ -51411,10 +51423,6 @@ function toPosixPath(input) {
51411
51423
  function trimDirectorySeparator(input) {
51412
51424
  return input.endsWith("/") ? input.slice(0, -1) : input;
51413
51425
  }
51414
- function parentPathOf(input) {
51415
- const separatorIndex = input.lastIndexOf("/");
51416
- return separatorIndex === -1 ? void 0 : input.slice(0, separatorIndex);
51417
- }
51418
51426
  function toProjectEntry(item) {
51419
51427
  const normalizedPath = trimDirectorySeparator(toPosixPath(item.item.relativePath));
51420
51428
  if (!normalizedPath) return null;
@@ -51517,19 +51525,41 @@ function isWholeWordRange(line, range) {
51517
51525
  const rightIsBoundary = range.end >= line.length || !isWord(codePointAt(line, range.end)) || !isWord(codePointBefore(line, range.end));
51518
51526
  return leftIsBoundary && rightIsBoundary;
51519
51527
  }
51520
- function withDirectoryAncestors(entries) {
51521
- const entryByPath = new Map(entries.map((entry) => [entry.path, entry]));
51522
- for (const entry of entries) {
51523
- let parentPath = parentPathOf(entry.path);
51524
- while (parentPath) {
51525
- if (!entryByPath.has(parentPath)) entryByPath.set(parentPath, {
51526
- path: parentPath,
51527
- kind: "directory"
51528
+ async function listWorkspaceFilesystemEntries(cwd, maxEntries) {
51529
+ const entries = [];
51530
+ const directories = [{
51531
+ absolutePath: cwd,
51532
+ relativePath: ""
51533
+ }];
51534
+ let truncated = false;
51535
+ while (directories.length > 0 && entries.length < maxEntries) {
51536
+ const current = directories.pop();
51537
+ if (!current) break;
51538
+ const dirents = await NodeFSP.readdir(current.absolutePath, { withFileTypes: true });
51539
+ dirents.sort((left, right) => left.name.localeCompare(right.name));
51540
+ for (const dirent of dirents) {
51541
+ if (entries.length >= maxEntries) {
51542
+ truncated = true;
51543
+ break;
51544
+ }
51545
+ if (dirent.isDirectory() && WORKSPACE_LIST_SKIPPED_DIRECTORIES.has(dirent.name)) continue;
51546
+ const relativePath = current.relativePath ? `${current.relativePath}/${dirent.name}` : dirent.name;
51547
+ const entry = {
51548
+ path: relativePath,
51549
+ kind: dirent.isDirectory() ? "directory" : "file"
51550
+ };
51551
+ entries.push(entry);
51552
+ if (entry.kind === "directory") directories.push({
51553
+ absolutePath: NodePath.join(current.absolutePath, dirent.name),
51554
+ relativePath
51528
51555
  });
51529
- parentPath = parentPathOf(parentPath);
51530
51556
  }
51531
51557
  }
51532
- return [...entryByPath.values()];
51558
+ if (directories.length > 0) truncated = true;
51559
+ return {
51560
+ entries: entries.toSorted((left, right) => left.path.localeCompare(right.path)),
51561
+ truncated
51562
+ };
51533
51563
  }
51534
51564
  const createFinder = fn("WorkspaceSearchIndex.createFinder")(function* (cwd, variant) {
51535
51565
  const result = yield* try_({
@@ -51619,13 +51649,16 @@ const make$16 = fn("WorkspaceSearchIndex.make")(function* (cwd, variant = "paths
51619
51649
  }));
51620
51650
  });
51621
51651
  const list = fn("WorkspaceSearchIndex.list")(function* () {
51622
- const mapped = mapMixedSearchResult(yield* runSearch("", WORKSPACE_INDEX_PAGE_SIZE, "mixedSearch", () => finder.mixedSearch("", { pageSize: WORKSPACE_INDEX_PAGE_SIZE })), WORKSPACE_INDEX_MAX_ENTRIES);
51623
- const sortedEntries = withDirectoryAncestors(mapped.entries).toSorted((left, right) => left.path.localeCompare(right.path));
51624
- const entries = sortedEntries.slice(0, WORKSPACE_INDEX_MAX_ENTRIES);
51625
- return {
51626
- entries,
51627
- truncated: mapped.truncated || entries.length < sortedEntries.length
51628
- };
51652
+ return yield* tryPromise({
51653
+ try: () => listWorkspaceFilesystemEntries(cwd, WORKSPACE_INDEX_MAX_ENTRIES),
51654
+ catch: (cause) => new WorkspaceSearchIndexSearchFailed({
51655
+ cwd,
51656
+ queryLength: 0,
51657
+ pageSize: WORKSPACE_INDEX_MAX_ENTRIES,
51658
+ reason: "Workspace filesystem listing failed.",
51659
+ cause
51660
+ })
51661
+ });
51629
51662
  });
51630
51663
  const search = fn("WorkspaceSearchIndex.search")(function* (query, limit, kind) {
51631
51664
  const pageSize = Math.max(1, limit + 1);
@@ -59153,14 +59186,23 @@ function buildClaudeImageContentBlock(input) {
59153
59186
  };
59154
59187
  }
59155
59188
  const buildUserMessageEffect = fn("buildUserMessageEffect")(function* (input, dependencies) {
59156
- const text = buildPromptText(input, dependencies.boundInstanceId);
59189
+ const prepared = prepareProviderAttachmentInput({
59190
+ text: buildPromptText(input, dependencies.boundInstanceId),
59191
+ attachmentsDir: dependencies.attachmentsDir,
59192
+ attachments: input.attachments ?? []
59193
+ });
59194
+ if (!prepared) return yield* new ProviderAdapterRequestError({
59195
+ provider: PROVIDER$2,
59196
+ method: "turn/start",
59197
+ detail: "Invalid file attachment id."
59198
+ });
59199
+ const text = prepared.text ?? "";
59157
59200
  const sdkContent = [];
59158
59201
  if (text.length > 0) sdkContent.push({
59159
59202
  type: "text",
59160
59203
  text
59161
59204
  });
59162
- for (const attachment of input.attachments ?? []) {
59163
- if (attachment.type !== "image") continue;
59205
+ for (const attachment of prepared.imageAttachments) {
59164
59206
  if (!SUPPORTED_CLAUDE_IMAGE_MIME_TYPES.has(attachment.mimeType)) return yield* new ProviderAdapterRequestError({
59165
59207
  provider: PROVIDER$2,
59166
59208
  method: "turn/start",
@@ -82089,6 +82131,17 @@ function mapToRuntimeEvents(event, canonicalThreadId) {
82089
82131
  if (event.method === "item/completed") {
82090
82132
  const item = readPayload(V2ItemCompletedNotification, event.payload)?.item;
82091
82133
  if (!item) return [];
82134
+ if (item.type === "imageGeneration") {
82135
+ const savedPath = trimText(item.savedPath);
82136
+ if (item.status === "completed" && savedPath) return [{
82137
+ ...runtimeEventBase(event, canonicalThreadId),
82138
+ type: "content.delta",
82139
+ payload: {
82140
+ streamKind: "assistant_text",
82141
+ delta: `![Generated image](${savedPath})`
82142
+ }
82143
+ }];
82144
+ }
82092
82145
  const itemType = toCanonicalItemType(item.type);
82093
82146
  if (itemType === "plan") {
82094
82147
  const detail = itemDetail(itemType, item);
@@ -82618,12 +82671,22 @@ const makeCodexAdapter = fn("makeCodexAdapter")(function* (codexConfig, options)
82618
82671
  };
82619
82672
  });
82620
82673
  const sendTurn = fn("sendTurn")(function* (input) {
82621
- const codexAttachments = yield* forEach(input.attachments ?? [], (attachment) => resolveAttachment(input, attachment), { concurrency: 1 });
82674
+ const prepared = prepareProviderAttachmentInput({
82675
+ text: input.input,
82676
+ attachmentsDir: serverConfig.attachmentsDir,
82677
+ attachments: input.attachments ?? []
82678
+ });
82679
+ if (!prepared) return yield* new ProviderAdapterRequestError({
82680
+ provider: PROVIDER,
82681
+ method: "turn/start",
82682
+ detail: "Invalid file attachment id."
82683
+ });
82684
+ const codexAttachments = yield* forEach(prepared.imageAttachments, (attachment) => resolveAttachment(input, attachment), { concurrency: 1 });
82622
82685
  const session = yield* requireSession(input.threadId);
82623
82686
  const reasoningEffort = input.modelSelection?.instanceId === boundInstanceId ? getModelSelectionStringOptionValue(input.modelSelection, "reasoningEffort") : void 0;
82624
82687
  const serviceTier = input.modelSelection?.instanceId === boundInstanceId ? getCodexServiceTierOptionValue(input.modelSelection) : void 0;
82625
82688
  return yield* session.runtime.sendTurn({
82626
- ...input.input !== void 0 ? { input: input.input } : {},
82689
+ ...prepared.text !== void 0 ? { input: prepared.text } : {},
82627
82690
  ...input.modelSelection?.instanceId === boundInstanceId ? { model: input.modelSelection.model } : {},
82628
82691
  ...reasoningEffort ? { effort: reasoningEffort } : {},
82629
82692
  ...serviceTier ? { serviceTier } : {},
@@ -88895,8 +88958,18 @@ function makeAcpAgentAdapter(descriptor, settings, options) {
88895
88958
  requestedModelId: requestedTurnModelId,
88896
88959
  mapError: (cause) => mapAcpToAdapterError(PROVIDER, input.threadId, "session/set_model", cause)
88897
88960
  });
88898
- const text = input.input?.trim();
88899
- const imagePromptParts = yield* forEach(input.attachments ?? [], (attachment) => gen(function* () {
88961
+ const preparedAttachments = prepareProviderAttachmentInput({
88962
+ text: input.input,
88963
+ attachmentsDir: serverConfig.attachmentsDir,
88964
+ attachments: input.attachments ?? []
88965
+ });
88966
+ if (!preparedAttachments) return yield* new ProviderAdapterRequestError({
88967
+ provider: PROVIDER,
88968
+ method: "session/prompt",
88969
+ detail: "Invalid file attachment id."
88970
+ });
88971
+ const text = preparedAttachments.text;
88972
+ const imagePromptParts = yield* forEach(preparedAttachments.imageAttachments, (attachment) => gen(function* () {
88900
88973
  const attachmentPath = resolveAttachmentPath({
88901
88974
  attachmentsDir: serverConfig.attachmentsDir,
88902
88975
  attachment