veryfront 0.1.1042 → 0.1.1043

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.
@@ -18,11 +18,13 @@ export type VeryfrontConfig = InferSchema<ReturnType<typeof getVeryfrontConfigSc
18
18
  export declare const getResolvedConfigSchema: () => import("../../src/internal-agents/schema.js").Schema<import("../../src/extensions/schema/schema-validator.js").InferShape<{
19
19
  apiUrl: import("../../src/internal-agents/schema.js").Schema<string>;
20
20
  apiToken: import("../../src/internal-agents/schema.js").Schema<string>;
21
+ apiTokenSource: import("../../src/internal-agents/schema.js").Schema<string | undefined>;
21
22
  projectSlug: import("../../src/internal-agents/schema.js").Schema<string>;
22
23
  }>>;
23
24
  export declare const ResolvedConfigSchema: import("../../src/internal-agents/schema.js").Schema<import("../../src/extensions/schema/schema-validator.js").InferShape<{
24
25
  apiUrl: import("../../src/internal-agents/schema.js").Schema<string>;
25
26
  apiToken: import("../../src/internal-agents/schema.js").Schema<string>;
27
+ apiTokenSource: import("../../src/internal-agents/schema.js").Schema<string | undefined>;
26
28
  projectSlug: import("../../src/internal-agents/schema.js").Schema<string>;
27
29
  }>>;
28
30
  export type ResolvedConfig = InferSchema<ReturnType<typeof getResolvedConfigSchema>>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/shared/config.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAGxE,OAAO,EAAE,KAAK,iBAAiB,EAAwB,MAAM,2BAA2B,CAAC;AA6BzF,eAAO,MAAM,wBAAwB;;IAGjC,mDAAmD;;;;GAKtD,CAAC;AACF,eAAO,MAAM,qBAAqB;;IAN9B,mDAAmD;;;;GAMkB,CAAC;AAC1E,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC,CAAC;AAEvF,eAAO,MAAM,uBAAuB;;;;GAMnC,CAAC;AACF,eAAO,MAAM,oBAAoB;;;;GAAsC,CAAC;AACxE,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAC;AAErF,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CA2CxF;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAatF;AA4ED,eAAO,MAAM,aAAa,gBAJH,MAAM,QAAQ,iBAAiB,KAAG,OAAO,CAAC,cAAc,CAIvB,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,gBAZX,MAAM,QAAQ,iBAAiB,KAAG,OAAO,CAAC,cAAc,CAYhB,CAAC;AAUhE,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACrC;AAED,eAAO,MAAM,iBAAiB;;;;GAM7B,CAAC;AACF,eAAO,MAAM,cAAc;;;;GAAgC,CAAC;AAC5D,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC;AAEzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CA+GjE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/shared/config.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAGxE,OAAO,EAAE,KAAK,iBAAiB,EAAwB,MAAM,2BAA2B,CAAC;AA8BzF,eAAO,MAAM,wBAAwB;;IAGjC,mDAAmD;;;;GAKtD,CAAC;AACF,eAAO,MAAM,qBAAqB;;IAN9B,mDAAmD;;;;GAMkB,CAAC;AAC1E,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC,CAAC;AAEvF,eAAO,MAAM,uBAAuB;;;;;GAOnC,CAAC;AACF,eAAO,MAAM,oBAAoB;;;;;GAAsC,CAAC;AACxE,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAC;AAGrF,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CA2CxF;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAatF;AAmHD,eAAO,MAAM,aAAa,gBAJH,MAAM,QAAQ,iBAAiB,KAAG,OAAO,CAAC,cAAc,CAIvB,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,gBAZX,MAAM,QAAQ,iBAAiB,KAAG,OAAO,CAAC,cAAc,CAYhB,CAAC;AAUhE,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACrC;AAED,eAAO,MAAM,iBAAiB;;;;GAM7B,CAAC;AACF,eAAO,MAAM,cAAc;;;;GAAgC,CAAC;AAC5D,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC;AAEzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CAuHjE"}
@@ -9,6 +9,7 @@ import { defineSchema, lazySchema } from "../../src/schemas/index.js";
9
9
  import { join } from "../../src/platform/compat/path/index.js";
10
10
  import { createFileSystem, cwd, getEnv } from "../../src/platform/index.js";
11
11
  import { getEnvironmentConfig } from "../../src/config/index.js";
12
+ import { getEnvSource } from "../../src/utils/env-loader.js";
12
13
  import { cliLogger, VERSION } from "../utils/index.js";
13
14
  import { readToken } from "../auth/token-store.js";
14
15
  import { ensureAuthenticated } from "../auth/login.js";
@@ -44,6 +45,7 @@ export const VeryfrontConfigSchema = lazySchema(getVeryfrontConfigSchema);
44
45
  export const getResolvedConfigSchema = defineSchema((v) => v.object({
45
46
  apiUrl: v.string(),
46
47
  apiToken: v.string(),
48
+ apiTokenSource: v.enum(["env", "env-file", "config-file", "token-store"]).optional(),
47
49
  projectSlug: v.string(),
48
50
  }));
49
51
  export const ResolvedConfigSchema = lazySchema(getResolvedConfigSchema);
@@ -130,16 +132,44 @@ function resolveTenantProjectReference() {
130
132
  getEnv("TENANT_PROJECT_ID") ||
131
133
  undefined;
132
134
  }
135
+ async function resolveApiTokenForMode(env, configFile, interactive) {
136
+ const envToken = env.apiToken;
137
+ const envSource = envToken ? getEnvSource("VERYFRONT_API_TOKEN") : { source: "unset" };
138
+ const storedToken = await readToken(env);
139
+ if (envToken && envSource.source !== "env-file") {
140
+ return {
141
+ apiToken: envToken,
142
+ apiTokenSource: "env",
143
+ };
144
+ }
145
+ if (configFile?.apiToken) {
146
+ return { apiToken: configFile.apiToken, apiTokenSource: "config-file" };
147
+ }
148
+ if (interactive && envToken && envSource.source === "env-file" && storedToken) {
149
+ return { apiToken: storedToken, apiTokenSource: "token-store" };
150
+ }
151
+ if (envToken) {
152
+ return {
153
+ apiToken: envToken,
154
+ apiTokenSource: envSource.source === "env-file" ? "env-file" : "env",
155
+ };
156
+ }
157
+ if (storedToken) {
158
+ return { apiToken: storedToken, apiTokenSource: "token-store" };
159
+ }
160
+ return { apiToken: null };
161
+ }
133
162
  async function resolveConfigBase(projectDir, env, interactive) {
134
163
  const dir = projectDir ?? cwd();
135
164
  const configFile = await readConfigFile(dir);
136
165
  const apiUrl = resolveCliApiUrl(env, configFile?.apiUrl);
137
- let apiToken = env.apiToken ?? configFile?.apiToken ?? (await readToken(env));
166
+ let { apiToken, apiTokenSource } = await resolveApiTokenForMode(env, configFile, interactive);
138
167
  if (!apiToken && interactive) {
139
168
  const userInfo = await ensureAuthenticated(env);
140
169
  if (!userInfo)
141
170
  throw new Error("Authentication required for this operation.");
142
171
  apiToken = (await readToken(env)) ?? null;
172
+ apiTokenSource = apiToken ? "token-store" : undefined;
143
173
  if (!apiToken)
144
174
  throw new Error("Authentication failed. Please try again.");
145
175
  }
@@ -153,7 +183,7 @@ async function resolveConfigBase(projectDir, env, interactive) {
153
183
  if (!projectSlug) {
154
184
  throw new Error("Could not determine project reference. Set VERYFRONT_PROJECT_SLUG, TENANT_PROJECT_SLUG, VERYFRONT_PROJECT_ID, or add projectSlug to veryfront.config.ts");
155
185
  }
156
- return { apiUrl, apiToken, projectSlug };
186
+ return { apiUrl, apiToken, ...(apiTokenSource ? { apiTokenSource } : {}), projectSlug };
157
187
  }
158
188
  function createConfigResolver(interactive) {
159
189
  return (projectDir, env) => resolveConfigByMode(projectDir, env, interactive);
@@ -177,6 +207,13 @@ export const getApiErrorSchema = defineSchema((v) => v.object({
177
207
  export const ApiErrorSchema = lazySchema(getApiErrorSchema);
178
208
  export function createApiClient(config) {
179
209
  const { apiUrl, apiToken } = config;
210
+ function addTokenSourceHint(message, status) {
211
+ if (config.apiTokenSource !== "env-file")
212
+ return message;
213
+ if (status !== 401 && status !== 403 && status !== 404)
214
+ return message;
215
+ return `${message}. VERYFRONT_API_TOKEN was loaded from a project .env file. For management commands, run 'veryfront login' and remove or rename the project runtime token, or pass a management token explicitly in the shell.`;
216
+ }
180
217
  async function requestOnce(method, url, body) {
181
218
  const response = await fetch(url, {
182
219
  method,
@@ -199,6 +236,7 @@ export function createApiClient(config) {
199
236
  catch {
200
237
  // Keep default error message if JSON parsing fails
201
238
  }
239
+ errorMessage = addTokenSourceHint(errorMessage, response.status);
202
240
  const err = new Error(errorMessage);
203
241
  err.status = response.status;
204
242
  throw err;
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.1042",
3
+ "version": "0.1.1043",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -9,6 +9,7 @@ export declare class StatOperations extends VeryfrontOperationsBase {
9
9
  private pathMapping;
10
10
  private readonly apiSearchCircuitBreaker;
11
11
  stat(path: string): Promise<FileInfo>;
12
+ private statWithoutSpan;
12
13
  private ensureIndexBuilt;
13
14
  private buildIndex;
14
15
  clearIndex(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"stat-operations.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/stat-operations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAuB/D,qBAAa,cAAe,SAAQ,uBAAuB;IACzD,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,aAAa,CAA8B;IAEnD,OAAO,CAAC,sBAAsB,CAA6B;IAC3D,OAAO,CAAC,qBAAqB,CAA8B;IAE3D,OAAO,CAAC,WAAW,CAAkC;IAErD,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAGrC;IAEH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;YAkHvB,gBAAgB;YAwChB,UAAU;IAkDxB,UAAU,IAAI,IAAI;IAMlB,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;YAIpC,cAAc;IAU5B,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,qBAAqB;YAQf,sBAAsB;YA8EtB,iBAAiB;IAS/B,OAAO,CAAC,gBAAgB;IA6DlB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWtC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CA0F1F"}
1
+ {"version":3,"file":"stat-operations.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/stat-operations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAiC/D,qBAAa,cAAe,SAAQ,uBAAuB;IACzD,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,aAAa,CAA8B;IAEnD,OAAO,CAAC,sBAAsB,CAA6B;IAC3D,OAAO,CAAC,qBAAqB,CAA8B;IAE3D,OAAO,CAAC,WAAW,CAAkC;IAErD,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAGrC;IAEH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;YAIvB,eAAe;YA4Gf,gBAAgB;YAwChB,UAAU;IAkDxB,UAAU,IAAI,IAAI;IAMlB,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;YAIpC,cAAc;IAU5B,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,qBAAqB;YAQf,sBAAsB;YA8EtB,iBAAiB;IAS/B,OAAO,CAAC,gBAAgB;IA6DlB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBtC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CA0F1F"}
@@ -1,7 +1,8 @@
1
1
  import { logger as baseLogger } from "../../../../utils/index.js";
2
2
  import { isFrameworkSourcePath } from "../../../../utils/path-utils.js";
3
3
  import { VeryfrontOperationsBase } from "./base-operations.js";
4
- import { createError, toError } from "../../../../errors/index.js";
4
+ import { createError, toError, VeryfrontError } from "../../../../errors/index.js";
5
+ import { fromError } from "../../../../errors/veryfront-error.js";
5
6
  import { buildStatCacheKeyPrefix } from "./cache-keys.js";
6
7
  import { STAT_OPERATION_EXTENSION_PRIORITY as EXTENSION_PRIORITY } from "./extension-priority.js";
7
8
  import { collectParentDirectories, normalizeIndexedFilePath, resolveByExtensionPriority, resolveIndexByExtensionPriority, sortPathsByExtensionPriority, stripKnownExtension, } from "./stat-operations-helpers.js";
@@ -12,6 +13,13 @@ const logger = baseLogger.component("stat-operations");
12
13
  const NOT_FOUND_SENTINEL = "__NOT_FOUND__";
13
14
  const API_SEARCH_CIRCUIT_BREAKER_THRESHOLD = 5;
14
15
  const API_SEARCH_CIRCUIT_BREAKER_COOLDOWN_MS = 30_000;
16
+ function isFileNotFoundError(error) {
17
+ if (error instanceof VeryfrontError && error.slug === "file-not-found") {
18
+ return true;
19
+ }
20
+ const veryfrontError = fromError(error);
21
+ return veryfrontError?.type === "file" && veryfrontError.message.startsWith("File not found:");
22
+ }
15
23
  export class StatOperations extends VeryfrontOperationsBase {
16
24
  fileIndex = null;
17
25
  directoryIndex = null;
@@ -24,100 +32,101 @@ export class StatOperations extends VeryfrontOperationsBase {
24
32
  cooldownMs: API_SEARCH_CIRCUIT_BREAKER_COOLDOWN_MS,
25
33
  });
26
34
  stat(path) {
27
- return withSpan("fs.veryfront.stat", async () => {
28
- const normalizedPath = this.normalizer.normalize(path);
29
- const ctx = this.contextProvider?.getContentContext();
30
- const cacheKey = `${buildStatCacheKeyPrefix(ctx)}:${normalizedPath}`;
31
- logger.debug("stat called", { path, normalizedPath, cacheKey });
32
- await this.ensureIndexBuilt();
33
- const fileIdx = this.fileIndex;
34
- const dirIdx = this.directoryIndex;
35
- if (!fileIdx || !dirIdx) {
36
- logger.debug("stat - no index available", { normalizedPath });
37
- throw toError(createError({
38
- type: "file",
39
- message: `Index not available for: ${normalizedPath}`,
40
- }));
41
- }
42
- const file = fileIdx.get(normalizedPath);
43
- if (file) {
44
- logger.debug("stat found file", { normalizedPath });
45
- return {
46
- size: file.size,
47
- mtime: new Date(file.updated_at),
48
- isDirectory: false,
49
- isFile: true,
50
- isSymlink: false,
51
- };
52
- }
53
- if (dirIdx.has(normalizedPath)) {
54
- logger.debug("stat found directory", { normalizedPath });
55
- return {
56
- size: 0,
57
- mtime: new Date(),
58
- isDirectory: true,
59
- isFile: false,
60
- isSymlink: false,
61
- };
62
- }
63
- // File not in index - try API pattern search as fallback for project files
64
- // Skip for framework paths (node_modules, _veryfront, etc.)
65
- if (!isFrameworkSourcePath(normalizedPath) && this.apiSearchCircuitBreaker.canSearch()) {
66
- const hasKnownExt = EXTENSION_PRIORITY.some((ext) => normalizedPath.endsWith(ext));
67
- if (hasKnownExt) {
68
- logger.debug("stat file not in index, trying API search", {
69
- normalizedPath,
70
- indexSize: fileIdx.size,
71
- });
72
- try {
73
- // Search for the exact file path
74
- const matches = await this.client.searchFiles(normalizedPath);
75
- this.apiSearchCircuitBreaker.recordSuccess();
76
- const exactMatch = matches.find((m) => m.path === normalizedPath);
77
- if (exactMatch) {
78
- logger.debug("stat found via API search", { normalizedPath });
79
- // Add to index for future lookups
80
- fileIdx.set(normalizedPath, {
81
- id: exactMatch.id,
82
- version_id: undefined,
83
- path: normalizedPath,
84
- content: undefined,
85
- type: "file",
86
- size: 0,
87
- updated_at: new Date().toISOString(),
88
- });
89
- return {
90
- size: 0,
91
- mtime: new Date(),
92
- isDirectory: false,
93
- isFile: true,
94
- isSymlink: false,
95
- };
96
- }
35
+ return withSpan("fs.veryfront.stat", () => this.statWithoutSpan(path), { "fs.path": path });
36
+ }
37
+ async statWithoutSpan(path) {
38
+ const normalizedPath = this.normalizer.normalize(path);
39
+ const ctx = this.contextProvider?.getContentContext();
40
+ const cacheKey = `${buildStatCacheKeyPrefix(ctx)}:${normalizedPath}`;
41
+ logger.debug("stat called", { path, normalizedPath, cacheKey });
42
+ await this.ensureIndexBuilt();
43
+ const fileIdx = this.fileIndex;
44
+ const dirIdx = this.directoryIndex;
45
+ if (!fileIdx || !dirIdx) {
46
+ logger.debug("stat - no index available", { normalizedPath });
47
+ throw toError(createError({
48
+ type: "file",
49
+ message: `Index not available for: ${normalizedPath}`,
50
+ }));
51
+ }
52
+ const file = fileIdx.get(normalizedPath);
53
+ if (file) {
54
+ logger.debug("stat found file", { normalizedPath });
55
+ return {
56
+ size: file.size,
57
+ mtime: new Date(file.updated_at),
58
+ isDirectory: false,
59
+ isFile: true,
60
+ isSymlink: false,
61
+ };
62
+ }
63
+ if (dirIdx.has(normalizedPath)) {
64
+ logger.debug("stat found directory", { normalizedPath });
65
+ return {
66
+ size: 0,
67
+ mtime: new Date(),
68
+ isDirectory: true,
69
+ isFile: false,
70
+ isSymlink: false,
71
+ };
72
+ }
73
+ // File not in index - try API pattern search as fallback for project files
74
+ // Skip for framework paths (node_modules, _veryfront, etc.)
75
+ if (!isFrameworkSourcePath(normalizedPath) && this.apiSearchCircuitBreaker.canSearch()) {
76
+ const hasKnownExt = EXTENSION_PRIORITY.some((ext) => normalizedPath.endsWith(ext));
77
+ if (hasKnownExt) {
78
+ logger.debug("stat file not in index, trying API search", {
79
+ normalizedPath,
80
+ indexSize: fileIdx.size,
81
+ });
82
+ try {
83
+ // Search for the exact file path
84
+ const matches = await this.client.searchFiles(normalizedPath);
85
+ this.apiSearchCircuitBreaker.recordSuccess();
86
+ const exactMatch = matches.find((m) => m.path === normalizedPath);
87
+ if (exactMatch) {
88
+ logger.debug("stat found via API search", { normalizedPath });
89
+ // Add to index for future lookups
90
+ fileIdx.set(normalizedPath, {
91
+ id: exactMatch.id,
92
+ version_id: undefined,
93
+ path: normalizedPath,
94
+ content: undefined,
95
+ type: "file",
96
+ size: 0,
97
+ updated_at: new Date().toISOString(),
98
+ });
99
+ return {
100
+ size: 0,
101
+ mtime: new Date(),
102
+ isDirectory: false,
103
+ isFile: true,
104
+ isSymlink: false,
105
+ };
97
106
  }
98
- catch (error) {
99
- const result = this.apiSearchCircuitBreaker.recordFailure();
100
- if (result.tripped) {
101
- logger.warn("stat API search circuit breaker tripped", {
102
- failures: result.failures,
103
- });
104
- }
105
- logger.debug("stat API search failed", {
106
- normalizedPath,
107
- error: error instanceof Error ? error.message : String(error),
107
+ }
108
+ catch (error) {
109
+ const result = this.apiSearchCircuitBreaker.recordFailure();
110
+ if (result.tripped) {
111
+ logger.warn("stat API search circuit breaker tripped", {
112
+ failures: result.failures,
108
113
  });
109
114
  }
115
+ logger.debug("stat API search failed", {
116
+ normalizedPath,
117
+ error: error instanceof Error ? error.message : String(error),
118
+ });
110
119
  }
111
120
  }
112
- logger.debug("stat file not found (not in index)", {
113
- normalizedPath,
114
- indexSize: fileIdx.size,
115
- });
116
- throw toError(createError({
117
- type: "file",
118
- message: `File not found: ${normalizedPath}`,
119
- }));
120
- }, { "fs.path": path });
121
+ }
122
+ logger.debug("stat file not found (not in index)", {
123
+ normalizedPath,
124
+ indexSize: fileIdx.size,
125
+ });
126
+ throw toError(createError({
127
+ type: "file",
128
+ message: `File not found: ${normalizedPath}`,
129
+ }));
121
130
  }
122
131
  async ensureIndexBuilt() {
123
132
  if (this.fileIndex && this.directoryIndex) {
@@ -357,15 +366,18 @@ export class StatOperations extends VeryfrontOperationsBase {
357
366
  return null;
358
367
  }
359
368
  async exists(path) {
360
- const normalizedPath = this.normalizer.normalize(path);
361
- try {
362
- await this.stat(normalizedPath);
363
- return true;
364
- }
365
- catch (_) {
366
- /* expected: stat throws when file does not exist */
367
- return false;
368
- }
369
+ return withSpan("fs.veryfront.exists", async () => {
370
+ try {
371
+ await this.statWithoutSpan(path);
372
+ return true;
373
+ }
374
+ catch (error) {
375
+ if (isFileNotFoundError(error)) {
376
+ return false;
377
+ }
378
+ throw error;
379
+ }
380
+ }, { "fs.path": path });
369
381
  }
370
382
  async resolveFile(basePath, options) {
371
383
  const resolveStart = performance.now();
@@ -1 +1 @@
1
- {"version":3,"file":"path-safety.d.ts","sourceRoot":"","sources":["../../../src/src/skill/path-safety.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAkFtE;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EAAE,EACxB,SAAS,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAmEjB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,MAAM,EAAE,CAAC,CAkBnB"}
1
+ {"version":3,"file":"path-safety.d.ts","sourceRoot":"","sources":["../../../src/src/skill/path-safety.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AA2FtE;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EAAE,EACxB,SAAS,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAmEjB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,MAAM,EAAE,CAAC,CA2BnB"}
@@ -10,11 +10,19 @@ import * as dntShim from "../../_dnt.shims.js";
10
10
  import { validatePath } from "../security/index.js";
11
11
  import { isAbsolute, join, relative, resolve } from "../platform/compat/path/index.js";
12
12
  import { exists, readDir, stat } from "../platform/compat/fs.js";
13
- import { createError, toError } from "../errors/veryfront-error.js";
13
+ import { VeryfrontError } from "../errors/index.js";
14
+ import { createError, fromError, toError } from "../errors/veryfront-error.js";
14
15
  function isInsideDir(baseDir, targetPath) {
15
16
  const rel = relative(baseDir, targetPath);
16
17
  return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
17
18
  }
19
+ function isFileNotFoundError(error) {
20
+ if (error instanceof VeryfrontError && error.slug === "file-not-found") {
21
+ return true;
22
+ }
23
+ const veryfrontError = fromError(error);
24
+ return veryfrontError?.type === "file" && veryfrontError.message.startsWith("File not found:");
25
+ }
18
26
  async function pathExists(path, fsAdapter) {
19
27
  return fsAdapter ? await fsAdapter.exists(path) : await exists(path);
20
28
  }
@@ -148,7 +156,16 @@ export async function validateSkillPath(skillRoot, requestedPath, allowedSubdirs
148
156
  */
149
157
  export async function listSkillSubdir(skillRoot, subdir, fsAdapter) {
150
158
  const dirPath = join(skillRoot, subdir);
151
- const dirExists = fsAdapter ? await fsAdapter.exists(dirPath) : await exists(dirPath);
159
+ let dirExists;
160
+ try {
161
+ dirExists = fsAdapter ? await fsAdapter.exists(dirPath) : await exists(dirPath);
162
+ }
163
+ catch (error) {
164
+ if (isFileNotFoundError(error)) {
165
+ return [];
166
+ }
167
+ throw error;
168
+ }
152
169
  if (!dirExists) {
153
170
  return [];
154
171
  }
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.1042";
2
+ export declare const VERSION = "0.1.1043";
3
3
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
3
  /** Shared version value. */
4
- export const VERSION = "0.1.1042";
4
+ export const VERSION = "0.1.1043";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.1042",
3
+ "version": "0.1.1043",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -330,9 +330,9 @@
330
330
  "@types/react": "19.2.14",
331
331
  "@types/react-dom": "19.2.3",
332
332
  "ws": "8.21.0",
333
- "@veryfront/ext-bundler-esbuild": "0.1.1042",
334
- "@veryfront/ext-content-mdx": "0.1.1042",
335
- "@veryfront/ext-css-tailwind": "0.1.1042"
333
+ "@veryfront/ext-bundler-esbuild": "0.1.1043",
334
+ "@veryfront/ext-content-mdx": "0.1.1043",
335
+ "@veryfront/ext-css-tailwind": "0.1.1043"
336
336
  },
337
337
  "devDependencies": {
338
338
  "@types/node": "20.9.0"