@mastra/platform-workspace 1.5.0-alpha.0 → 1.5.0-alpha.2

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/index.js CHANGED
@@ -1788,9 +1788,10 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1788
1788
  }
1789
1789
  lastLease = lease;
1790
1790
  attemptsMade = attempt + 1;
1791
+ const leaseCwd = options?.cwd ?? this.workingDirectory;
1791
1792
  const execOptions = {
1792
1793
  command: fullCommand,
1793
- ...options?.cwd !== void 0 && { cwd: options.cwd },
1794
+ ...leaseCwd !== void 0 && { cwd: leaseCwd },
1794
1795
  ...filteredEnv !== void 0 && { env: filteredEnv },
1795
1796
  ...effectiveTimeout != null && effectiveTimeout > 0 && { timeoutMs: effectiveTimeout },
1796
1797
  ...this._webSocketFactory && { webSocketFactory: this._webSocketFactory }
@@ -1827,9 +1828,10 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1827
1828
  */
1828
1829
  async _tryExecViaPrivateNetwork(instanceUrl, fullCommand, effectiveTimeout, options) {
1829
1830
  const filteredEnv = this._execEnv(options);
1831
+ const privateNetCwd = options?.cwd ?? this.workingDirectory;
1830
1832
  const execOptions = {
1831
1833
  command: fullCommand,
1832
- ...options?.cwd !== void 0 && { cwd: options.cwd },
1834
+ ...privateNetCwd !== void 0 && { cwd: privateNetCwd },
1833
1835
  ...filteredEnv !== void 0 && { env: filteredEnv },
1834
1836
  ...effectiveTimeout != null && effectiveTimeout > 0 && { timeoutMs: effectiveTimeout },
1835
1837
  ...this._privateNetFetch && { fetch: this._privateNetFetch }
@@ -1963,38 +1965,71 @@ const CLONE_URL_SEGMENT_PATTERN = /^[\w.-]+$/;
1963
1965
  * pins repositories to their current default-branch commit. Private repository
1964
1966
  * credentials are used for head resolution and sent to the provider as
1965
1967
  * transient build envs; they never enter the serialized definition. If the
1966
- * head cannot be resolved, the resolver returns undefined so PlatformSandbox
1967
- * boots from the provider default and the caller's runtime setup materializes
1968
- * the checkout instead.
1968
+ * repository or its head cannot be resolved, the resolver keeps `cpuCount` and
1969
+ * `memoryMB` in a resources-only template so the sandbox still boots at the
1970
+ * requested size, and the caller's runtime setup materializes the checkout.
1969
1971
  */
1970
1972
  function createRepoTemplate(options) {
1971
1973
  const getRepositoryAccess = options.getRepositoryAccess;
1972
- if (!getRepositoryAccess) return void 0;
1974
+ const resourcesOnly = () => {
1975
+ if (options.cpuCount === void 0 && options.memoryMB === void 0) return void 0;
1976
+ return withResources(Template(), options);
1977
+ };
1978
+ if (!getRepositoryAccess) {
1979
+ const template = resourcesOnly();
1980
+ return template ? async () => template : void 0;
1981
+ }
1973
1982
  const resolveHead = options.resolveHead ?? resolveDefaultBranchHead;
1974
1983
  return async () => {
1975
- const access = await getRepositoryAccess().catch(() => void 0);
1976
- if (!access?.cloneUrl) return void 0;
1984
+ let accessError;
1985
+ const access = await getRepositoryAccess().catch((error) => {
1986
+ accessError = error;
1987
+ });
1988
+ if (!access?.cloneUrl) {
1989
+ console.warn("[platform-workspace] repo template skipped: repository access unavailable", { error: redactSecrets(accessError) });
1990
+ return resourcesOnly();
1991
+ }
1977
1992
  const cloneUrl = normalizeCloneUrl(access.cloneUrl);
1978
- if (!isValidCloneUrl(cloneUrl)) return void 0;
1993
+ if (!isValidCloneUrl(cloneUrl)) {
1994
+ console.warn("[platform-workspace] repo template skipped: clone URL failed validation", { cloneUrl: redactSecrets(cloneUrl) });
1995
+ return resourcesOnly();
1996
+ }
1979
1997
  const token = access.authorization?.token;
1980
- const sha = await (token ? resolveHead(cloneUrl, token) : resolveHead(cloneUrl)).catch(() => void 0);
1981
- if (!sha || !SHA_PATTERN.test(sha)) return void 0;
1982
- const workdir = defaultWorkdir(cloneUrl);
1998
+ let headError;
1999
+ const sha = await (token ? resolveHead(cloneUrl, token) : resolveHead(cloneUrl)).catch((error) => {
2000
+ headError = error;
2001
+ });
2002
+ if (!sha || !SHA_PATTERN.test(sha)) {
2003
+ console.warn("[platform-workspace] repo template skipped: could not resolve default-branch head", {
2004
+ cloneUrl,
2005
+ sha,
2006
+ error: redactSecrets(headError)
2007
+ });
2008
+ return resourcesOnly();
2009
+ }
2010
+ const workingDirectory = options.workingDirectory === void 0 ? void 0 : trimTrailingSlashes(assertWorkingDirectory(options.workingDirectory));
2011
+ const repoDir = repoDirName(cloneUrl);
1983
2012
  const auth = token ? `${gitAuthFlag()} ` : "";
1984
- const steps = [
1985
- `git ${auth}clone ${cloneUrl} "${workdir}"`,
1986
- `git -C "${workdir}" ${auth}fetch origin ${sha}`,
1987
- `git -C "${workdir}" checkout ${sha}`,
1988
- ...options.setupCommand ? [`cd "${workdir}" && ${options.setupCommand}`] : []
1989
- ];
1990
- const family = `repo:${cloneUrl}:${workdir}`;
2013
+ const setupCommands = (options.setupCommand === void 0 ? [] : Array.isArray(options.setupCommand) ? options.setupCommand : [options.setupCommand]).filter((command) => command.trim() !== "");
2014
+ const family = `repo:${cloneUrl}:${workingDirectory ?? ""}/${repoDir}`;
1991
2015
  let template = Template();
1992
- if (token) template = template.setEnvs({ [BUILD_TOKEN_ENV]: token }, { ephemeral: true });
1993
- if (options.cpuCount !== void 0) template = template.cpuCount(options.cpuCount);
1994
- if (options.memoryMB !== void 0) template = template.memoryMB(options.memoryMB);
1995
- return template.runCmd(steps).withFamily(family);
2016
+ const buildEnv = {
2017
+ ...options.buildEnv,
2018
+ ...token ? { [BUILD_TOKEN_ENV]: token } : {}
2019
+ };
2020
+ if (Object.keys(buildEnv).length > 0) template = template.setEnvs(buildEnv, { ephemeral: true });
2021
+ template = withResources(template, options);
2022
+ if (workingDirectory) template = template.runCmd(`mkdir -p "${workingDirectory}"`).setWorkdir(workingDirectory);
2023
+ template = template.runCmd(`git ${auth}clone ${cloneUrl} "${repoDir}"`).runCmd(`git -C "${repoDir}" ${auth}fetch origin ${sha}`).runCmd(`git -C "${repoDir}" checkout ${sha}`);
2024
+ for (const command of setupCommands) template = template.runCmd(`cd "${repoDir}" && ${command}`);
2025
+ return template.withFamily(family);
1996
2026
  };
1997
2027
  }
2028
+ function withResources(template, options) {
2029
+ if (options.cpuCount !== void 0) template = template.cpuCount(options.cpuCount);
2030
+ if (options.memoryMB !== void 0) template = template.memoryMB(options.memoryMB);
2031
+ return template;
2032
+ }
1998
2033
  function isValidCloneUrl(cloneUrl) {
1999
2034
  if (cloneUrl.length > 2048 || !CLONE_URL_ALLOWED_CHARS.test(cloneUrl)) return false;
2000
2035
  let url;
@@ -2020,8 +2055,26 @@ function normalizeCloneUrl(cloneUrl) {
2020
2055
  return `${scheme.toLowerCase()}${host.toLowerCase()}`;
2021
2056
  });
2022
2057
  }
2023
- function defaultWorkdir(cloneUrl) {
2024
- return `$HOME/${(normalizeCloneUrl(cloneUrl).split("/").at(-1) ?? "").replace(/[^\w.-]/g, "-").replace(/^\.+/, "") || "repo"}`;
2058
+ function repoDirName(cloneUrl) {
2059
+ return (normalizeCloneUrl(cloneUrl).split("/").at(-1) ?? "").replace(/[^\w.-]/g, "-").replace(/^\.+/, "") || "repo";
2060
+ }
2061
+ function trimTrailingSlashes(path) {
2062
+ let end = path.length;
2063
+ while (end > 1 && path[end - 1] === "/") end--;
2064
+ return path.slice(0, end);
2065
+ }
2066
+ /** Validate a literal absolute path before embedding it in shell build steps. */
2067
+ function assertWorkingDirectory(dir) {
2068
+ if (!(/^\/[A-Za-z0-9._/-]*$/.test(dir) && !dir.split("/").includes(".."))) throw new Error(`Repo template workingDirectory must be an absolute path of plain path characters (got ${JSON.stringify(dir)}); ~ and $HOME are not expanded.`);
2069
+ return dir;
2070
+ }
2071
+ /**
2072
+ * Render a caught value for a warning without leaking credentials: URL
2073
+ * userinfo, HTTP authorization values, and GitHub token shapes are masked.
2074
+ */
2075
+ function redactSecrets(value) {
2076
+ if (value === void 0 || value === null) return void 0;
2077
+ return (value instanceof Error ? value.message : typeof value === "string" ? value : String(value)).replace(/\/\/[^/@\s]+@/g, "//***@").replace(/\b(bearer|basic)\s+[^\s"']+/gi, "$1 ***").replace(/\b(gh[pousr]_|github_pat_)[A-Za-z0-9_]+/g, "$1***");
2025
2078
  }
2026
2079
  function gitAuthFlag() {
2027
2080
  return `-c http.extraheader="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$${BUILD_TOKEN_ENV}" | base64 -w0)"`;
@@ -2049,8 +2102,10 @@ async function resolveDefaultBranchHead(cloneUrl, token, execute = execFileAsync
2049
2102
  });
2050
2103
  const sha = stdout.trim().split(/\s+/, 1)[0];
2051
2104
  return sha && SHA_PATTERN.test(sha) ? sha : void 0;
2052
- } catch {
2053
- return;
2105
+ } catch (error) {
2106
+ const stderr = error.stderr;
2107
+ const detail = typeof stderr === "string" && stderr.trim() ? stderr.trim() : String(error);
2108
+ throw new Error(`git ls-remote failed: ${detail}`);
2054
2109
  }
2055
2110
  }
2056
2111
  //#endregion