@meistrari/tela-cli 0.7.47 → 0.7.48

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
@@ -6285,6 +6285,7 @@ var require_setup = __commonJS({
6285
6285
  exports2.createGitSshKey = createGitSshKey;
6286
6286
  exports2.resolveCloneRootPath = resolveCloneRootPath;
6287
6287
  exports2.listDiscoveredSshPublicKeys = listDiscoveredSshPublicKeys;
6288
+ exports2.prepareAgentSetupSshKeys = prepareAgentSetupSshKeys;
6288
6289
  exports2.prepareAgentSetup = prepareAgentSetup;
6289
6290
  exports2.findAgentSetupOrganization = findAgentSetupOrganization;
6290
6291
  exports2.validatePreparedAgentSetupInput = validatePreparedAgentSetupInput;
@@ -6296,7 +6297,9 @@ var require_setup = __commonJS({
6296
6297
  var tela_local_config_1 = require_dist();
6297
6298
  var agent_api_1 = require_agent_api();
6298
6299
  var credentials_1 = require_credentials2();
6300
+ var errors_1 = require_errors();
6299
6301
  var config_1 = require_config2();
6302
+ var process_1 = require_process();
6300
6303
  function getHomeDir() {
6301
6304
  return process.env.HOME || (0, node_os_1.homedir)();
6302
6305
  }
@@ -6398,6 +6401,134 @@ var require_setup = __commonJS({
6398
6401
  }
6399
6402
  return (0, node_fs_1.readdirSync)(sshDirectory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".pub")).map((entry) => loadSshPublicKey((0, node_path_1.join)(sshDirectory, entry.name))).sort((left, right) => left.title.localeCompare(right.title) || left.path.localeCompare(right.path));
6400
6403
  }
6404
+ function listDiscoveredSshKeyPairs(sshDirectory = getSshDirectory()) {
6405
+ return listDiscoveredSshPublicKeys(sshDirectory).map((key) => ({
6406
+ ...key,
6407
+ privateKeyPath: inferSshPrivateKeyPath(key.path)
6408
+ })).filter((key) => (0, node_fs_1.existsSync)(key.privateKeyPath));
6409
+ }
6410
+ function toCreatedSshKey(key) {
6411
+ return {
6412
+ title: key.title,
6413
+ path: key.path,
6414
+ privateKeyPath: key.privateKeyPath
6415
+ };
6416
+ }
6417
+ function hasUsableSavedSshKey(setup) {
6418
+ const sshKey = setup?.gitSshKey;
6419
+ return Boolean(sshKey?.publicKeyPath && sshKey.privateKeyPath && (0, node_fs_1.existsSync)(sshKey.publicKeyPath) && (0, node_fs_1.existsSync)(sshKey.privateKeyPath));
6420
+ }
6421
+ function createSavedSshKeyOption(sshKey) {
6422
+ return {
6423
+ ...loadSshPublicKey(sshKey.publicKeyPath),
6424
+ privateKeyPath: resolveSshPrivateKeyPath(sshKey.privateKeyPath)
6425
+ };
6426
+ }
6427
+ function includePreferredSshKeyOption(sshPublicKeys, preferredKey) {
6428
+ return [
6429
+ preferredKey,
6430
+ ...sshPublicKeys.filter((key) => key.path !== preferredKey.path)
6431
+ ];
6432
+ }
6433
+ function ensureSshDirectory(sshDirectory) {
6434
+ const exists = (0, node_fs_1.existsSync)(sshDirectory);
6435
+ if (!exists) {
6436
+ (0, node_fs_1.mkdirSync)(sshDirectory, { recursive: true, mode: 448 });
6437
+ }
6438
+ const stats = (0, node_fs_1.statSync)(sshDirectory);
6439
+ if (!stats.isDirectory()) {
6440
+ throw (0, errors_1.createUserFacingError)({
6441
+ code: "ssh_directory_invalid",
6442
+ summary: `SSH directory path is not a directory: ${sshDirectory}`,
6443
+ fixes: ["Move or remove that path, then retry Tela CLI setup."]
6444
+ });
6445
+ }
6446
+ if (!exists) {
6447
+ (0, node_fs_1.chmodSync)(sshDirectory, 448);
6448
+ }
6449
+ }
6450
+ function getAvailableGeneratedSshKeyPath(sshDirectory) {
6451
+ const basePath = (0, node_path_1.join)(sshDirectory, "tela_agent_ed25519");
6452
+ const maxAttempts = 1e3;
6453
+ for (let index = 0; index < maxAttempts; index += 1) {
6454
+ const privateKeyPath = index === 0 ? basePath : `${basePath}_${index + 1}`;
6455
+ const publicKeyPath = `${privateKeyPath}.pub`;
6456
+ if (!(0, node_fs_1.existsSync)(privateKeyPath) && !(0, node_fs_1.existsSync)(publicKeyPath)) {
6457
+ return privateKeyPath;
6458
+ }
6459
+ }
6460
+ throw (0, errors_1.createUserFacingError)({
6461
+ code: "ssh_key_path_unavailable",
6462
+ summary: `Could not find an available Tela SSH key path in ${sshDirectory}.`,
6463
+ fixes: ["Remove unused tela_agent_ed25519 key files or create an SSH key manually, then retry setup."]
6464
+ });
6465
+ }
6466
+ async function createGeneratedSshKey(sshDirectory, options) {
6467
+ ensureSshDirectory(sshDirectory);
6468
+ const sshKeygenPath = await options.resolveExecutableFn("ssh-keygen");
6469
+ if (!sshKeygenPath) {
6470
+ throw (0, errors_1.createUserFacingError)({
6471
+ code: "ssh_keygen_missing",
6472
+ summary: "Could not create an SSH key because ssh-keygen was not found.",
6473
+ fixes: ["Install OpenSSH tools or create an ed25519 SSH key manually, then retry Tela CLI setup."]
6474
+ });
6475
+ }
6476
+ const privateKeyPath = getAvailableGeneratedSshKeyPath(sshDirectory);
6477
+ const result = await options.runCommandFn([
6478
+ sshKeygenPath,
6479
+ "-t",
6480
+ "ed25519",
6481
+ "-N",
6482
+ "",
6483
+ "-C",
6484
+ "tela-agent",
6485
+ "-f",
6486
+ privateKeyPath
6487
+ ], {
6488
+ allowFailure: true,
6489
+ timeoutMs: 15e3
6490
+ });
6491
+ if (result.exitCode !== 0) {
6492
+ const details = result.stderr.trim() || result.stdout.trim() || `ssh-keygen exited with code ${result.exitCode}.`;
6493
+ throw (0, errors_1.createUserFacingError)({
6494
+ code: "ssh_key_generation_failed",
6495
+ summary: "Could not create an SSH key for Tela CLI setup.",
6496
+ details,
6497
+ fixes: ["Create an ed25519 SSH key manually, then retry Tela CLI setup."]
6498
+ });
6499
+ }
6500
+ if (!(0, node_fs_1.existsSync)(privateKeyPath) || !(0, node_fs_1.existsSync)(`${privateKeyPath}.pub`)) {
6501
+ throw (0, errors_1.createUserFacingError)({
6502
+ code: "ssh_key_generation_missing_files",
6503
+ summary: "ssh-keygen completed but the expected SSH key files were not created.",
6504
+ fixes: ["Create an ed25519 SSH key manually, then retry Tela CLI setup."]
6505
+ });
6506
+ }
6507
+ (0, node_fs_1.chmodSync)(privateKeyPath, 384);
6508
+ return {
6509
+ ...loadSshPublicKey(`${privateKeyPath}.pub`),
6510
+ privateKeyPath
6511
+ };
6512
+ }
6513
+ async function prepareAgentSetupSshKeys(options = {}) {
6514
+ const sshDirectory = options.sshDirectory ?? getSshDirectory();
6515
+ const existingSshPublicKeys = listDiscoveredSshKeyPairs(sshDirectory);
6516
+ if (existingSshPublicKeys.length > 0) {
6517
+ return {
6518
+ sshPublicKeys: existingSshPublicKeys,
6519
+ createdSshKey: null
6520
+ };
6521
+ }
6522
+ const created = await createGeneratedSshKey(sshDirectory, {
6523
+ runCommandFn: options.runCommandFn ?? process_1.runCommand,
6524
+ resolveExecutableFn: options.resolveExecutableFn ?? process_1.resolveExecutable
6525
+ });
6526
+ const sshPublicKeys = listDiscoveredSshKeyPairs(sshDirectory);
6527
+ return {
6528
+ sshPublicKeys,
6529
+ createdSshKey: toCreatedSshKey(sshPublicKeys.find((key) => key.path === created.path) ?? created)
6530
+ };
6531
+ }
6401
6532
  function createSetupUser(credential, fallbackEmail) {
6402
6533
  const raw = toObject(credential?.raw);
6403
6534
  const rawUser = toObject(raw?.user);
@@ -6435,12 +6566,13 @@ var require_setup = __commonJS({
6435
6566
  if (organizations.length === 0) {
6436
6567
  throw new Error("No Agent API organizations are available for this Tela workspace. Ask the Tela team to register an organization, then try setup again.");
6437
6568
  }
6438
- const sshPublicKeys = listDiscoveredSshPublicKeys().map((key) => ({
6439
- ...key,
6440
- privateKeyPath: inferSshPrivateKeyPath(key.path)
6441
- })).filter((key) => (0, node_fs_1.existsSync)(key.privateKeyPath));
6442
- const selectedSshPublicKeyPath = existingSetup?.gitSshKey?.publicKeyPath ?? sshPublicKeys[0]?.path ?? "";
6443
- const selectedSshPrivateKeyPath = existingSetup?.gitSshKey?.privateKeyPath ?? sshPublicKeys.find((key) => key.path === selectedSshPublicKeyPath)?.privateKeyPath ?? (selectedSshPublicKeyPath ? inferSshPrivateKeyPath(selectedSshPublicKeyPath) : "");
6569
+ const existingSshKey = hasUsableSavedSshKey(existingSetup) ? existingSetup?.gitSshKey ?? null : null;
6570
+ const { sshPublicKeys, createdSshKey } = existingSshKey ? {
6571
+ sshPublicKeys: includePreferredSshKeyOption(listDiscoveredSshKeyPairs(), createSavedSshKeyOption(existingSshKey)),
6572
+ createdSshKey: null
6573
+ } : await prepareAgentSetupSshKeys();
6574
+ const selectedSshPublicKeyPath = existingSshKey ? existingSshKey.publicKeyPath : sshPublicKeys[0]?.path ?? "";
6575
+ const selectedSshPrivateKeyPath = existingSshKey?.privateKeyPath ?? sshPublicKeys.find((key) => key.path === selectedSshPublicKeyPath)?.privateKeyPath ?? (selectedSshPublicKeyPath ? inferSshPrivateKeyPath(selectedSshPublicKeyPath) : "");
6444
6576
  const selectedOrganizationId = existingSetup?.forgejoOrganization?.id ?? organizations[0]?.id ?? "";
6445
6577
  return {
6446
6578
  contextId: config.contextId,
@@ -6451,6 +6583,7 @@ var require_setup = __commonJS({
6451
6583
  sshPublicKeys,
6452
6584
  selectedSshPublicKeyPath,
6453
6585
  selectedSshPrivateKeyPath,
6586
+ createdSshKey,
6454
6587
  existingSetup
6455
6588
  };
6456
6589
  }
@@ -6469,10 +6602,19 @@ var require_setup = __commonJS({
6469
6602
  throw new Error(`Unknown Agent API organization: ${selector}`);
6470
6603
  }
6471
6604
  function validatePreparedAgentSetupInput(prepared, input) {
6605
+ if (Boolean(input.sshPublicKeyPath) !== Boolean(input.sshPrivateKeyPath)) {
6606
+ throw (0, errors_1.createUserFacingError)({
6607
+ code: "setup_ssh_key_override_pair_required",
6608
+ summary: "SSH key override paths must be provided together.",
6609
+ fixes: ["Pass both --ssh-public-key and --ssh-private-key, or omit both to use the selected key."]
6610
+ });
6611
+ }
6472
6612
  const organization = findAgentSetupOrganization(prepared.organizations, input.organization);
6473
6613
  const cloneRoot = input.cloneRoot ? resolveCloneRootPath(input.cloneRoot) : resolveCloneRootPath(prepared.cloneRoot);
6474
- const sshKey = loadSshPublicKey(input.sshPublicKeyPath);
6475
- const sshPrivateKeyPath = resolveSshPrivateKeyPath(input.sshPrivateKeyPath);
6614
+ const sshPublicKeyPath = input.sshPublicKeyPath || prepared.selectedSshPublicKeyPath;
6615
+ const sshPrivateKeyPathInput = input.sshPrivateKeyPath || prepared.selectedSshPrivateKeyPath;
6616
+ const sshKey = loadSshPublicKey(sshPublicKeyPath);
6617
+ const sshPrivateKeyPath = resolveSshPrivateKeyPath(sshPrivateKeyPathInput);
6476
6618
  return {
6477
6619
  contextId: prepared.contextId,
6478
6620
  organization,
@@ -7095,9 +7237,7 @@ var require_cli = __commonJS({
7095
7237
  function assertSetupRequiredOptions(options) {
7096
7238
  const missing = [
7097
7239
  options.contextId ? "" : "--context",
7098
- options.organization ? "" : "--organization",
7099
- options.sshPublicKeyPath ? "" : "--ssh-public-key",
7100
- options.sshPrivateKeyPath ? "" : "--ssh-private-key"
7240
+ options.organization ? "" : "--organization"
7101
7241
  ].filter(Boolean);
7102
7242
  if (missing.length > 0) {
7103
7243
  throw (0, errors_1.createUserFacingError)({
@@ -7105,7 +7245,7 @@ var require_cli = __commonJS({
7105
7245
  summary: `Missing required setup option${missing.length === 1 ? "" : "s"}: ${missing.join(", ")}`,
7106
7246
  fixes: [
7107
7247
  "Run `tela agent setup --list-options --context <context-id> --json` to inspect valid values.",
7108
- "Pass all required flags for non-interactive setup."
7248
+ "Pass the required context and organization flags for non-interactive setup."
7109
7249
  ]
7110
7250
  });
7111
7251
  }
@@ -7130,6 +7270,7 @@ var require_cli = __commonJS({
7130
7270
  })),
7131
7271
  selectedSshPublicKeyPath: preparation2.selectedSshPublicKeyPath,
7132
7272
  selectedSshPrivateKeyPath: preparation2.selectedSshPrivateKeyPath,
7273
+ createdSshKey: preparation2.createdSshKey,
7133
7274
  existingSetup: preparation2.existingSetup
7134
7275
  };
7135
7276
  if (options.json)
@@ -7142,6 +7283,12 @@ var require_cli = __commonJS({
7142
7283
  { label: "Clone root", value: result2.cloneRoot }
7143
7284
  ])),
7144
7285
  (0, ui_1.section)("Organizations", result2.organizations.map((item) => `- ${item.name} (${item.id})`).join("\n")),
7286
+ ...result2.createdSshKey ? [
7287
+ (0, ui_1.section)("Generated SSH key", (0, ui_1.kvList)([
7288
+ { label: "Public key", value: result2.createdSshKey.path },
7289
+ { label: "Private key", value: result2.createdSshKey.privateKeyPath }
7290
+ ]))
7291
+ ] : [],
7145
7292
  (0, ui_1.section)("SSH keys", result2.sshPublicKeys.map((item) => `- ${item.title}: ${item.path}`).join("\n") || "<none>")
7146
7293
  ]));
7147
7294
  }
@@ -7166,6 +7313,7 @@ var require_cli = __commonJS({
7166
7313
  cloneRoot: validated.cloneRoot,
7167
7314
  sshPublicKeyPath: validated.sshKey.path,
7168
7315
  sshPrivateKeyPath: validated.sshPrivateKeyPath,
7316
+ createdSshKey: preparation.createdSshKey,
7169
7317
  nextActions: ["Run the same setup command without `--dry-run` to provision Forgejo and save setup metadata."]
7170
7318
  };
7171
7319
  if (options.json)
@@ -7179,6 +7327,12 @@ var require_cli = __commonJS({
7179
7327
  { label: "SSH public key", value: result2.sshPublicKeyPath },
7180
7328
  { label: "SSH private key", value: result2.sshPrivateKeyPath }
7181
7329
  ])),
7330
+ ...result2.createdSshKey ? [
7331
+ (0, ui_1.section)("Generated SSH key", (0, ui_1.kvList)([
7332
+ { label: "Public key", value: result2.createdSshKey.path },
7333
+ { label: "Private key", value: result2.createdSshKey.privateKeyPath }
7334
+ ]))
7335
+ ] : [],
7182
7336
  (0, ui_1.nextActions)(result2.nextActions)
7183
7337
  ]));
7184
7338
  }
@@ -7196,6 +7350,7 @@ var require_cli = __commonJS({
7196
7350
  contextId: setup.contextId,
7197
7351
  setup: setup.setup,
7198
7352
  response: setup.response,
7353
+ createdSshKey: preparation.createdSshKey,
7199
7354
  nextActions: ["Run `tela agent sync --all --dry-run --json` to inspect repository sync readiness."]
7200
7355
  };
7201
7356
  if (options.json)
@@ -7207,6 +7362,12 @@ var require_cli = __commonJS({
7207
7362
  { label: "Forgejo Organization", value: setup.setup.forgejoOrganization?.name ?? "<none>" },
7208
7363
  { label: "SSH key", value: setup.setup.gitSshKey?.title ?? "<none>" }
7209
7364
  ])),
7365
+ ...result.createdSshKey ? [
7366
+ (0, ui_1.section)("Generated SSH key", (0, ui_1.kvList)([
7367
+ { label: "Public key", value: result.createdSshKey.path },
7368
+ { label: "Private key", value: result.createdSshKey.privateKeyPath }
7369
+ ]))
7370
+ ] : [],
7210
7371
  (0, ui_1.nextActions)(result.nextActions)
7211
7372
  ]));
7212
7373
  }
@@ -8629,7 +8790,7 @@ function writeHelp(io) {
8629
8790
  writeLine(io, " agent run [--input <name=path>...] [--mode one-shot|chat] [--input-conflict fail|overwrite|rename] [--plain] [--json] [--verbose]");
8630
8791
  writeLine(io, " agent push [--message <text>|--claude|--manual] [--dry-run] [--json] [--verbose]");
8631
8792
  writeLine(io, " agent feedback [--list] [--apply all|select] [--feedback-id <id>...] [--json] [--verbose]");
8632
- writeLine(io, " agent setup [--list-options] [--context <context-id>] [--organization <id-or-name>] [--clone-root <path>] [--ssh-public-key <path>] [--ssh-private-key <path>] [--dry-run] [--json]");
8793
+ writeLine(io, " agent setup [--list-options] --context <context-id> --organization <id-or-name> [--clone-root <path>] [--ssh-public-key <path>] [--ssh-private-key <path>] [--dry-run] [--json]");
8633
8794
  }
8634
8795
  function getPackageVersion() {
8635
8796
  const metadata = JSON.parse((0, import_node_fs2.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/tela-cli",
3
- "version": "0.7.47",
3
+ "version": "0.7.48",
4
4
  "description": "Tela command line tools for local state and agent workflows.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -18,12 +18,14 @@ tela agent setup \
18
18
  --context production/foundation \
19
19
  --organization acme \
20
20
  --clone-root "$HOME/tela-agents" \
21
- --ssh-public-key "$HOME/.ssh/id_ed25519.pub" \
22
- --ssh-private-key "$HOME/.ssh/id_ed25519" \
23
21
  --dry-run \
24
22
  --json
25
23
  ```
26
24
 
25
+ Setup uses an existing SSH key pair from `~/.ssh`, or creates
26
+ `~/.ssh/tela_agent_ed25519` when no key pair exists. Pass `--ssh-public-key`
27
+ and `--ssh-private-key` only when overriding that default selection.
28
+
27
29
  Complete setup by removing `--dry-run`. This provisions the Forgejo user and
28
30
  saves Agent setup metadata in the shared Tela local config.
29
31