@united-robotics/cli 0.4.3 → 0.4.5

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 +56 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,23 +2,29 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import { chmodSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
5
+ import { chmodSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
6
6
  import { homedir, tmpdir } from "os";
7
- import { basename, join } from "path";
7
+ import { basename, dirname, join, parse } from "path";
8
8
  import { spawnSync } from "child_process";
9
- var configDir = join(homedir(), ".united-robotics");
10
- var configPath = join(configDir, "config.json");
9
+ var configFileName = "config.json";
10
+ var configDirName = ".united-robotics";
11
+ var globalConfigDir = join(homedir(), configDirName);
12
+ var globalConfigPath = join(globalConfigDir, configFileName);
11
13
  var defaultApiUrl = process.env.UR_API_URL ?? "https://united-robotics.rollersoft.com.au";
12
14
  function load() {
13
- try {
14
- return JSON.parse(readFileSync(configPath, "utf8"));
15
- } catch {
16
- return { apiUrl: defaultApiUrl };
15
+ const localPath = findLocalConfigPath(process.cwd());
16
+ for (const path of [process.env.UR_CONFIG, localPath, globalConfigPath].filter(Boolean)) {
17
+ try {
18
+ return JSON.parse(readFileSync(path, "utf8"));
19
+ } catch {
20
+ }
17
21
  }
22
+ return { apiUrl: defaultApiUrl };
18
23
  }
19
- function save(config) {
20
- mkdirSync(configDir, { recursive: true });
21
- writeFileSync(configPath, JSON.stringify(config, null, 2));
24
+ function save(config, opts = {}) {
25
+ const path = opts.global ? globalConfigPath : localConfigPath(process.cwd());
26
+ mkdirSync(dirname(path), { recursive: true });
27
+ writeFileSync(path, JSON.stringify(config, null, 2));
22
28
  }
23
29
  async function api(path, init = {}) {
24
30
  const cfg = load();
@@ -28,10 +34,10 @@ async function api(path, init = {}) {
28
34
  }
29
35
  var program = new Command();
30
36
  program.name("ur").description("United Robotics customer CLI").version("0.1.0");
31
- program.command("login").requiredOption("--token <token>").option("--api <url>").action((opts) => {
37
+ program.command("login").requiredOption("--token <token>").option("--api <url>").option("--global", "write ~/.united-robotics/config.json instead of the current workspace").action((opts) => {
32
38
  const cfg = load();
33
- save({ apiUrl: opts.api ?? cfg.apiUrl ?? defaultApiUrl, token: opts.token });
34
- console.log("Logged in to United Robotics");
39
+ save({ apiUrl: opts.api ?? cfg.apiUrl ?? defaultApiUrl, token: opts.token }, { global: opts.global });
40
+ console.log(`Logged in to United Robotics (${opts.global ? "global" : "workspace"} config)`);
35
41
  });
36
42
  program.command("whoami").action(async () => console.log(JSON.stringify(await api("/api/me"), null, 2)));
37
43
  program.command("team").argument("<cmd>").action(async (cmd) => {
@@ -50,10 +56,15 @@ project.command("clone").argument("<projectId>").option("--dest <path>").option(
50
56
  try {
51
57
  if (opts.inPlace && opts.dest) throw new Error("Use either --in-place or --dest, not both.");
52
58
  const dest = opts.inPlace ? "." : opts.dest;
53
- if (opts.inPlace) assertInPlaceCloneTarget(process.cwd());
54
- const cloneArgs = ["clone", primary.url];
55
- if (dest) cloneArgs.push(dest);
56
- runGit(cloneArgs, { cwd: process.cwd(), env: credentials.env, authHint: true });
59
+ const preservedConfig = opts.inPlace ? preserveInPlaceWorkspaceConfig(process.cwd()) : null;
60
+ try {
61
+ if (opts.inPlace) assertInPlaceCloneTarget(process.cwd());
62
+ const cloneArgs = ["clone", primary.url];
63
+ if (dest) cloneArgs.push(dest);
64
+ runGit(cloneArgs, { cwd: process.cwd(), env: credentials.env, authHint: true });
65
+ } finally {
66
+ preservedConfig?.restore();
67
+ }
57
68
  const worktree = dest ?? repoDirName(primary.url);
58
69
  configureGitIdentity(worktree, opts.name, opts.email, credentials.env);
59
70
  if (opts.submodules) initProjectWorktree(worktree, opts.name, opts.email, credentials.env);
@@ -115,7 +126,7 @@ program.command("codex").argument("<cmd>").option("--project <projectId>", "proj
115
126
  if (cmd !== "onboard") throw new Error("supported: ur codex onboard");
116
127
  const cfg = load();
117
128
  const loginToken = opts.includeToken ? cfg.token ?? "<TOKEN>" : "<TOKEN>";
118
- console.log(`Establish new agent workspace in the current empty Codex project root by globally installing \`@united-robotics/cli\` module (ensuring there is a working node, nvm and git) and authenticate using token "ur login --token ${loginToken}". Then run \`ur project ls\` and \`ur project clone ${opts.project} --in-place\`; in-place clone puts AGENTS.md and workspace files at the Codex project root, while internally obtaining the short-lived project-wide GitHub token, initializing private submodules, and applying the Vercel-compatible git identity. When GitHub write access is needed manually, use \`ur github token --project ${opts.project}\` for a short-lived GitHub App token, but avoid printing that token in logs unless necessary. Use that project root as codex project base from now on, and familiarize yourself.`);
129
+ console.log(`Establish new agent workspace in the current empty Codex project root by globally installing \`@united-robotics/cli\` module (ensuring there is a working node, nvm and git) and authenticate using token "ur login --token ${loginToken}". Then run \`ur project ls\` and \`ur project clone ${opts.project} --in-place\`; in-place clone puts AGENTS.md and workspace files at the Codex project root, while internally obtaining the short-lived project-wide GitHub token, initializing private submodules, and applying the Vercel-compatible git identity. When GitHub write access is needed manually, use \`ur github token --project ${opts.project}\` for a short-lived GitHub App token, but avoid printing that token in logs unless necessary. Treat the cloned project root as the Codex project base. For any task that changes code/content/instructions, completion requires committing and pushing the intended changes to the appropriate remote branch; if you cannot push, report the exact blocker instead of saying the task is done. Familiarize yourself before editing.`);
119
130
  });
120
131
  program.parseAsync().catch((error) => {
121
132
  console.error(error instanceof Error ? error.message : String(error));
@@ -179,6 +190,32 @@ function assertInPlaceCloneTarget(cwd) {
179
190
  throw new Error(`Cannot in-place clone into a non-empty directory (${cwd}). Start from an empty Codex project root, or use --dest <path> intentionally.`);
180
191
  }
181
192
  }
193
+ function localConfigPath(cwd) {
194
+ return join(cwd, configDirName, configFileName);
195
+ }
196
+ function findLocalConfigPath(start) {
197
+ let dir = start;
198
+ const root = parse(start).root;
199
+ while (true) {
200
+ const candidate = localConfigPath(dir);
201
+ if (existsSync(candidate)) return candidate;
202
+ if (dir === root) return null;
203
+ dir = dirname(dir);
204
+ }
205
+ }
206
+ function preserveInPlaceWorkspaceConfig(cwd) {
207
+ const configDir = join(cwd, configDirName);
208
+ const configPath = join(configDir, configFileName);
209
+ if (!existsSync(configPath)) return null;
210
+ const content = readFileSync(configPath, "utf8");
211
+ rmSync(configDir, { recursive: true, force: true });
212
+ return {
213
+ restore: () => {
214
+ mkdirSync(configDir, { recursive: true });
215
+ writeFileSync(configPath, content);
216
+ }
217
+ };
218
+ }
182
219
  function gitBin() {
183
220
  return process.env.UR_GIT_BIN || (process.platform === "darwin" ? "/usr/bin/git" : "git");
184
221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@united-robotics/cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "ur": "dist/index.js"