securewithtrace 0.2.0 → 0.3.1

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.
Files changed (2) hide show
  1. package/dist/index.js +126 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3564,6 +3564,11 @@ function createAuthCommand() {
3564
3564
  return auth;
3565
3565
  }
3566
3566
 
3567
+ // src/repo/commands.ts
3568
+ import { chmodSync, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
3569
+ import { tmpdir } from "os";
3570
+ import { join as join2 } from "path";
3571
+
3567
3572
  // ../../node_modules/.bun/@ts-rest+core@3.53.0-rc.1+f9199aabcde8d6a0/node_modules/@ts-rest/core/index.esm.mjs
3568
3573
  var util;
3569
3574
  (function(util2) {
@@ -22596,6 +22601,13 @@ var RepositoryResponseSchema = external_exports.object({
22596
22601
  updatedAt: external_exports.date()
22597
22602
  });
22598
22603
  var ListRepositoriesResponseSchema = external_exports.array(RepositoryResponseSchema);
22604
+ var CloneTokenRequestSchema = external_exports.object({
22605
+ repositoryUrl: external_exports.string().url()
22606
+ });
22607
+ var CloneTokenResponseSchema = external_exports.object({
22608
+ token: external_exports.string(),
22609
+ expiresAt: external_exports.string().datetime()
22610
+ });
22599
22611
 
22600
22612
  // ../../packages/api-contracts/src/contracts/v1/repository.ts
22601
22613
  var c9 = initContract();
@@ -22661,6 +22673,19 @@ var repositoryContract = c9.router({
22661
22673
  404: ErrorResponseSchema,
22662
22674
  500: ErrorResponseSchema
22663
22675
  }
22676
+ },
22677
+ getCloneToken: {
22678
+ method: "POST",
22679
+ path: "/organizations/:organizationId/repositories/clone-token",
22680
+ pathParams: OrgIdParamsSchema,
22681
+ body: CloneTokenRequestSchema,
22682
+ responses: {
22683
+ 200: CloneTokenResponseSchema,
22684
+ 401: ErrorResponseSchema,
22685
+ 403: ErrorResponseSchema,
22686
+ 404: ErrorResponseSchema,
22687
+ 500: ErrorResponseSchema
22688
+ }
22664
22689
  }
22665
22690
  });
22666
22691
 
@@ -23738,6 +23763,7 @@ var FindingSchema = external_exports.object({
23738
23763
  });
23739
23764
 
23740
23765
  // src/api-client.ts
23766
+ var readTokenSafe = readToken;
23741
23767
  var PRODUCTION_API_URL = "https://api.securewithtrace.com";
23742
23768
  function getApiBaseUrl() {
23743
23769
  const raw = process.env["TRACE_API_URL"];
@@ -23799,6 +23825,25 @@ async function resolveOrganizationId(client) {
23799
23825
  }
23800
23826
  return id;
23801
23827
  }
23828
+ async function resolveOrganizationIdSafe(client) {
23829
+ try {
23830
+ const result = await client.organization.listOrganizations();
23831
+ if (result.status !== 200) {
23832
+ return null;
23833
+ }
23834
+ const org = result.body[0];
23835
+ if (!org) {
23836
+ return null;
23837
+ }
23838
+ const id = org.organizationId;
23839
+ if (!SAFE_ID_PATTERN.test(id)) {
23840
+ return null;
23841
+ }
23842
+ return id;
23843
+ } catch {
23844
+ return null;
23845
+ }
23846
+ }
23802
23847
 
23803
23848
  // src/repo/commands.ts
23804
23849
  function stripAnsi(str) {
@@ -23879,11 +23924,57 @@ function createListCommand() {
23879
23924
  }
23880
23925
  });
23881
23926
  }
23927
+ function urlWithUsername(httpsUrl) {
23928
+ const url2 = new URL(httpsUrl);
23929
+ url2.username = "x-access-token";
23930
+ url2.password = "";
23931
+ return url2.toString();
23932
+ }
23933
+ function createAskPassScript(token) {
23934
+ const dir = mkdtempSync(join2(tmpdir(), "trace-askpass-"));
23935
+ const scriptPath = join2(dir, "askpass.sh");
23936
+ writeFileSync2(scriptPath, `#!/bin/sh
23937
+ echo '${token.replace(/'/g, "'\\''")}'
23938
+ `, {
23939
+ mode: 448
23940
+ });
23941
+ chmodSync(scriptPath, 448);
23942
+ return scriptPath;
23943
+ }
23944
+ async function fetchCloneToken(repoUrl) {
23945
+ const token = readTokenSafe();
23946
+ if (!token) {
23947
+ return null;
23948
+ }
23949
+ try {
23950
+ const client = createClient(token);
23951
+ const organizationId = await resolveOrganizationIdSafe(client);
23952
+ if (!organizationId) {
23953
+ return null;
23954
+ }
23955
+ const result = await client.repository.getCloneToken({
23956
+ params: { organizationId },
23957
+ body: { repositoryUrl: repoUrl }
23958
+ });
23959
+ if (result.status === 200) {
23960
+ return result.body.token;
23961
+ }
23962
+ return null;
23963
+ } catch {
23964
+ return null;
23965
+ }
23966
+ }
23882
23967
  function createCloneCommand() {
23883
23968
  return new Command("clone").description("Clone a repository").argument("<repository>", "Repository URL or owner/repo shorthand").argument("[directory]", "Directory to clone into").option("--branch <branch>", "Branch to clone").option("--depth <depth>", "Shallow clone depth", Number.parseInt).action(
23884
23969
  async (repository, directory, options) => {
23885
23970
  const cloneUrl = resolveRepoUrl(repository);
23886
23971
  const targetDir = directory ?? extractRepoName(repository);
23972
+ let installationToken = null;
23973
+ if (cloneUrl.startsWith("https://github.com/")) {
23974
+ installationToken = await fetchCloneToken(cloneUrl);
23975
+ }
23976
+ const effectiveUrl = installationToken ? urlWithUsername(cloneUrl) : cloneUrl;
23977
+ let askPassScript;
23887
23978
  const args = ["clone"];
23888
23979
  if (options.branch) {
23889
23980
  validateBranchName(options.branch);
@@ -23892,17 +23983,43 @@ function createCloneCommand() {
23892
23983
  if (options.depth !== void 0) {
23893
23984
  args.push("--depth", String(options.depth));
23894
23985
  }
23895
- args.push("--", cloneUrl, targetDir);
23986
+ args.push("--", effectiveUrl, targetDir);
23896
23987
  console.log(`Cloning ${cloneUrl} into ${targetDir}...`);
23897
- const proc = Bun.spawn(["git", ...args], {
23898
- stdout: "inherit",
23899
- stderr: "inherit"
23900
- });
23901
- const exitCode = await proc.exited;
23902
- if (exitCode !== 0) {
23903
- process.exit(exitCode);
23988
+ const extraEnv = {};
23989
+ if (installationToken) {
23990
+ askPassScript = createAskPassScript(installationToken);
23991
+ extraEnv["GIT_ASKPASS"] = askPassScript;
23992
+ extraEnv["GIT_TERMINAL_PROMPT"] = "0";
23993
+ }
23994
+ try {
23995
+ const proc = Bun.spawn(["git", ...args], {
23996
+ stdout: "inherit",
23997
+ stderr: "inherit",
23998
+ env: { ...process.env, ...extraEnv }
23999
+ });
24000
+ const exitCode = await proc.exited;
24001
+ if (exitCode !== 0) {
24002
+ process.exit(exitCode);
24003
+ }
24004
+ if (installationToken) {
24005
+ const resetProc = Bun.spawn(["git", "remote", "set-url", "origin", cloneUrl], {
24006
+ cwd: targetDir,
24007
+ stdout: "pipe",
24008
+ stderr: "pipe"
24009
+ });
24010
+ const resetExitCode = await resetProc.exited;
24011
+ if (resetExitCode !== 0) {
24012
+ console.error(
24013
+ "Warning: failed to reset remote URL \u2014 the token username may persist in .git/config"
24014
+ );
24015
+ }
24016
+ }
24017
+ console.log(`Repository cloned into ${targetDir}`);
24018
+ } finally {
24019
+ if (askPassScript) {
24020
+ rmSync2(askPassScript.replace(/\/[^/]+$/, ""), { recursive: true, force: true });
24021
+ }
23904
24022
  }
23905
- console.log(`Repository cloned into ${targetDir}`);
23906
24023
  }
23907
24024
  );
23908
24025
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Trace CLI",
5
5
  "type": "module",
6
6
  "bin": {