@fractary/codex-cli 0.10.26 → 0.10.28

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/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import * as path4 from 'path';
3
3
  import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import { createConfigManager, resolveOrganization, validateNameFormat, formatBytes, createHealthChecker, readCodexConfig, formatDuration, createCodexClient, ValidationError, PermissionDeniedError, ConfigurationError, CodexError, CodexClient } from '@fractary/codex';
5
+ import { createConfigManager, resolveOrganization, validateNameFormat, formatBytes, createHealthChecker, readCodexConfig, calculateContentHash, formatDuration, createCodexClient, ValidationError, PermissionDeniedError, ConfigurationError, CodexError, CodexClient } from '@fractary/codex';
6
6
  import * as os from 'os';
7
7
  import * as fs2 from 'fs/promises';
8
8
  import { spawn } from 'child_process';
@@ -118,10 +118,13 @@ async function isValidGitRepo(repoPath) {
118
118
  return false;
119
119
  }
120
120
  }
121
- function getCodexRepoUrl(config) {
121
+ function getCodexRepoUrl(config, token) {
122
122
  const codexRepo = config.codex_repo || "codex";
123
123
  validateGitHubName(config.organization, "organization");
124
124
  validateGitHubName(codexRepo, "repository");
125
+ if (token) {
126
+ return `https://x-access-token:${encodeURIComponent(token)}@github.com/${config.organization}/${codexRepo}.git`;
127
+ }
125
128
  return `https://github.com/${config.organization}/${codexRepo}.git`;
126
129
  }
127
130
  async function execGit(repoPath, args) {
@@ -185,6 +188,10 @@ async function ensureCodexCloned(config, options) {
185
188
  const branch = options?.branch || "main";
186
189
  if (await isValidGitRepo(tempPath) && !options?.force) {
187
190
  try {
191
+ if (options?.token) {
192
+ const repoUrl2 = getCodexRepoUrl(config, options.token);
193
+ await execGit(tempPath, ["remote", "set-url", "origin", repoUrl2]);
194
+ }
188
195
  await gitFetch(tempPath, branch);
189
196
  await gitCheckout(tempPath, branch);
190
197
  await gitPull(tempPath);
@@ -195,7 +202,7 @@ async function ensureCodexCloned(config, options) {
195
202
  await fs2.rm(tempPath, { recursive: true, force: true });
196
203
  }
197
204
  }
198
- const repoUrl = getCodexRepoUrl(config);
205
+ const repoUrl = getCodexRepoUrl(config, options?.token);
199
206
  try {
200
207
  await fs2.rm(tempPath, { recursive: true, force: true });
201
208
  } catch (error) {
@@ -945,6 +952,23 @@ function syncCommand() {
945
952
  const cmd = new Command("sync");
946
953
  cmd.description("Sync single project with codex repository").argument("[name]", "Project name (auto-detected if not provided)").option("--env <env>", "Target environment (dev/test/staging/prod)", "prod").option("--dry-run", "Show what would sync without executing").option("--direction <dir>", "Sync direction (to-codex/from-codex/bidirectional)", "bidirectional").option("--include <pattern>", "Include files matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--exclude <pattern>", "Exclude files matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--force", "Force sync without checking timestamps").option("--json", "Output as JSON").option("--work-id <id>", "GitHub issue number or URL to scope sync to").action(async (name, options) => {
947
954
  try {
955
+ const envFilePath = path4.join(process.cwd(), ".fractary", "env", ".env");
956
+ try {
957
+ const { readFile } = await import('fs/promises');
958
+ const envContent = await readFile(envFilePath, "utf-8");
959
+ for (const line of envContent.split("\n")) {
960
+ const trimmed = line.trim();
961
+ if (!trimmed || trimmed.startsWith("#")) continue;
962
+ const eqIdx = trimmed.indexOf("=");
963
+ if (eqIdx === -1) continue;
964
+ const key = trimmed.slice(0, eqIdx).trim();
965
+ const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
966
+ if (key && !process.env[key]) {
967
+ process.env[key] = val;
968
+ }
969
+ }
970
+ } catch {
971
+ }
948
972
  const configPath = path4.join(process.cwd(), ".fractary", "config.yaml");
949
973
  let config;
950
974
  try {
@@ -976,6 +1000,7 @@ function syncCommand() {
976
1000
  }
977
1001
  const direction = options.direction;
978
1002
  const targetBranch = getEnvironmentBranch(config, options.env);
1003
+ const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN || void 0;
979
1004
  const localStorage = createLocalStorage({
980
1005
  baseDir: process.cwd()
981
1006
  });
@@ -1015,14 +1040,19 @@ function syncCommand() {
1015
1040
  console.error(chalk10.yellow(`Warning: Invalid pattern "${pattern}": ${error.message}`));
1016
1041
  }
1017
1042
  }
1043
+ const fsModuleLocal = await import('fs/promises');
1018
1044
  const targetFiles = await Promise.all(
1019
1045
  Array.from(matchedFilePaths).map(async (filePath) => {
1020
1046
  const fullPath = path4.join(sourceDir, filePath);
1021
- const stats = await import('fs/promises').then((fs3) => fs3.stat(fullPath));
1047
+ const [stats, content] = await Promise.all([
1048
+ fsModuleLocal.stat(fullPath),
1049
+ fsModuleLocal.readFile(fullPath)
1050
+ ]);
1022
1051
  return {
1023
1052
  path: filePath,
1024
1053
  size: stats.size,
1025
- mtime: stats.mtimeMs
1054
+ mtime: stats.mtimeMs,
1055
+ hash: calculateContentHash(content)
1026
1056
  };
1027
1057
  })
1028
1058
  );
@@ -1045,7 +1075,8 @@ function syncCommand() {
1045
1075
  console.log(chalk10.blue("\u2139 Cloning/updating codex repository..."));
1046
1076
  }
1047
1077
  codexRepoPath = await ensureCodexCloned2(config, {
1048
- branch: targetBranch
1078
+ branch: targetBranch,
1079
+ token
1049
1080
  });
1050
1081
  if (!options.json) {
1051
1082
  console.log(chalk10.dim(` Codex cloned to: ${codexRepoPath}`));
@@ -1101,7 +1132,8 @@ function syncCommand() {
1101
1132
  console.log(chalk10.blue("\u2139 Cloning/updating codex repository..."));
1102
1133
  }
1103
1134
  codexRepoPath = await ensureCodexCloned2(config, {
1104
- branch: targetBranch
1135
+ branch: targetBranch,
1136
+ token
1105
1137
  });
1106
1138
  if (!options.json) {
1107
1139
  console.log(chalk10.dim(` Codex cloned to: ${codexRepoPath}`));
@@ -1123,11 +1155,15 @@ function syncCommand() {
1123
1155
  existingCodexFiles = await Promise.all(
1124
1156
  codexGlobMatches.map(async (filePath) => {
1125
1157
  const fullPath = path4.join(codexProjectDir, filePath);
1126
- const stats = await fsPromises.stat(fullPath);
1158
+ const [stats, content] = await Promise.all([
1159
+ fsPromises.stat(fullPath),
1160
+ fsPromises.readFile(fullPath)
1161
+ ]);
1127
1162
  return {
1128
1163
  path: filePath,
1129
1164
  size: stats.size,
1130
- mtime: stats.mtimeMs
1165
+ mtime: stats.mtimeMs,
1166
+ hash: calculateContentHash(content)
1131
1167
  };
1132
1168
  })
1133
1169
  );