hereya-cli 0.85.6 → 0.86.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 (41) hide show
  1. package/README.md +130 -80
  2. package/dist/backend/index.js +24 -0
  3. package/dist/commands/app/deploy/index.d.ts +2 -0
  4. package/dist/commands/app/deploy/index.js +17 -0
  5. package/dist/commands/app/deployments/index.d.ts +4 -0
  6. package/dist/commands/app/deployments/index.js +20 -1
  7. package/dist/commands/app/destroy/index.d.ts +2 -0
  8. package/dist/commands/app/destroy/index.js +17 -0
  9. package/dist/commands/app/env/index.d.ts +2 -0
  10. package/dist/commands/app/env/index.js +17 -0
  11. package/dist/commands/app/list/index.d.ts +4 -0
  12. package/dist/commands/app/list/index.js +20 -1
  13. package/dist/commands/app/status/index.d.ts +2 -0
  14. package/dist/commands/app/status/index.js +17 -0
  15. package/dist/commands/deploy/index.d.ts +2 -0
  16. package/dist/commands/deploy/index.js +90 -55
  17. package/dist/commands/init/index.d.ts +2 -0
  18. package/dist/commands/init/index.js +18 -1
  19. package/dist/commands/login/index.js +5 -0
  20. package/dist/commands/publish/index.d.ts +2 -0
  21. package/dist/commands/publish/index.js +17 -0
  22. package/dist/commands/run/index.d.ts +2 -0
  23. package/dist/commands/run/index.js +17 -0
  24. package/dist/commands/search/index.d.ts +2 -0
  25. package/dist/commands/search/index.js +38 -21
  26. package/dist/commands/undeploy/index.d.ts +2 -0
  27. package/dist/commands/undeploy/index.js +50 -15
  28. package/dist/commands/up/index.d.ts +2 -0
  29. package/dist/commands/up/index.js +17 -1
  30. package/dist/commands/workspace/executor/install/index.d.ts +2 -0
  31. package/dist/commands/workspace/executor/install/index.js +17 -1
  32. package/dist/commands/workspace/executor/token/index.d.ts +2 -0
  33. package/dist/commands/workspace/executor/token/index.js +17 -0
  34. package/dist/commands/workspace/executor/uninstall/index.d.ts +2 -0
  35. package/dist/commands/workspace/executor/uninstall/index.js +17 -1
  36. package/dist/lib/ephemeral-token.d.ts +38 -0
  37. package/dist/lib/ephemeral-token.js +36 -0
  38. package/dist/lib/package/index.d.ts +12 -0
  39. package/dist/lib/package/index.js +36 -22
  40. package/oclif.manifest.json +133 -3
  41. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { getAwsConfig } from '../infrastructure/aws-config.js';
2
+ import { getEphemeralToken } from '../lib/ephemeral-token.js';
2
3
  import { CloudBackend } from './cloud/cloud-backend.js';
3
4
  import { loginWithToken, refreshToken } from './cloud/login.js';
4
5
  import { getCloudCredentials, loadBackendConfig, saveCloudCredentials } from './config.js';
@@ -66,6 +67,29 @@ export async function getBackend(typeOrOptions) {
66
67
  if (backend) {
67
68
  return backend;
68
69
  }
70
+ // Ephemeral, in-memory-only token (typically set via the --token flag or by
71
+ // an embedding integration). We use the supplied token directly as the
72
+ // bearer for cloud API calls and explicitly do NOT exchange it via
73
+ // `loginWithToken` — that would mint a long-lived refresh token and we
74
+ // want this code path to never persist anything to disk or the keychain.
75
+ const ephemeral = getEphemeralToken();
76
+ if (ephemeral) {
77
+ const url = ephemeral.cloudUrl
78
+ ?? process.env.HEREYA_CLOUD_URL
79
+ ?? (await loadBackendConfig()).cloud?.url
80
+ ?? 'https://cloud.hereya.dev';
81
+ backend = new CloudBackend({
82
+ accessToken: ephemeral.token,
83
+ // We have no client/refresh identity for an ephemeral token. Use empty
84
+ // strings — the CloudBackend never uses these for the request bearer
85
+ // (it only uses accessToken), and we never call refreshToken() in this
86
+ // mode.
87
+ clientId: '',
88
+ refreshToken: '',
89
+ url,
90
+ });
91
+ return backend;
92
+ }
69
93
  // Handle token-based auth
70
94
  if (typeOrOptions && typeof typeOrOptions === 'object' && typeOrOptions.token) {
71
95
  const url = typeOrOptions.url || 'https://cloud.hereya.dev';
@@ -9,10 +9,12 @@ export default class AppDeploy extends Command {
9
9
  chdir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
10
  local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  parameter: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
12
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  version: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  workspace: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
15
  };
15
16
  run(): Promise<void>;
17
+ private runInner;
16
18
  private runLocal;
17
19
  private runRemote;
18
20
  }
@@ -8,6 +8,8 @@ import { getBackend } from '../../../backend/index.js';
8
8
  import { LocalExecutor } from '../../../executor/local.js';
9
9
  import { resolveAndSubstituteAppParams, resolveAppParameters } from '../../../lib/app-parameters.js';
10
10
  import { getEnvManager } from '../../../lib/env/index.js';
11
+ import { withEphemeralToken } from '../../../lib/ephemeral-token.js';
12
+ import { getDefaultLogger } from '../../../lib/log.js';
11
13
  import { arrayOfStringToObject } from '../../../lib/object-utils.js';
12
14
  import { stripOrgPrefix } from '../../../lib/org-utils.js';
13
15
  import { getProfileFromWorkspace } from '../../../lib/profile-utils.js';
@@ -88,6 +90,10 @@ export default class AppDeploy extends Command {
88
90
  description: 'parameter for the app deployment, in the form of key=value (repeatable)',
89
91
  multiple: true,
90
92
  }),
93
+ token: Flags.string({
94
+ description: 'Ephemeral cloud access token used for this invocation only. Held in memory; never written to the keychain or `~/.hereya/secrets/`. Takes precedence over the HEREYA_TOKEN environment variable.',
95
+ required: false,
96
+ }),
91
97
  version: Flags.string({
92
98
  description: 'specific app version to deploy (defaults to latest)',
93
99
  }),
@@ -98,6 +104,17 @@ export default class AppDeploy extends Command {
98
104
  }),
99
105
  };
100
106
  async run() {
107
+ const { flags } = await this.parse(AppDeploy);
108
+ // Precedence: --token flag > HEREYA_TOKEN env var > keychain.
109
+ if (flags.token) {
110
+ if (process.env.HEREYA_TOKEN) {
111
+ getDefaultLogger().debug('[app deploy] --token flag overrides HEREYA_TOKEN environment variable.');
112
+ }
113
+ return withEphemeralToken(flags.token, () => this.runInner());
114
+ }
115
+ return this.runInner();
116
+ }
117
+ async runInner() {
101
118
  const { args, flags } = await this.parse(AppDeploy);
102
119
  const forceLocal = flags.local || process.env.HEREYA_LOCAL_EXECUTION === 'true';
103
120
  if (forceLocal) {
@@ -5,5 +5,9 @@ export default class AppDeployments extends Command {
5
5
  };
6
6
  static description: string;
7
7
  static examples: string[];
8
+ static flags: {
9
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
8
11
  run(): Promise<void>;
12
+ private runInner;
9
13
  }
@@ -1,13 +1,32 @@
1
- import { Args, Command } from '@oclif/core';
1
+ import { Args, Command, Flags } from '@oclif/core';
2
2
  import { CloudBackend } from '../../../backend/cloud/cloud-backend.js';
3
3
  import { getBackend } from '../../../backend/index.js';
4
+ import { withEphemeralToken } from '../../../lib/ephemeral-token.js';
5
+ import { getDefaultLogger } from '../../../lib/log.js';
4
6
  export default class AppDeployments extends Command {
5
7
  static args = {
6
8
  name: Args.string({ description: 'app name in org/name format', required: true }),
7
9
  };
8
10
  static description = 'List workspaces a hereya-app has been deployed to.';
9
11
  static examples = ['<%= config.bin %> <%= command.id %> my-org/my-app'];
12
+ static flags = {
13
+ token: Flags.string({
14
+ description: 'Ephemeral cloud access token used for this invocation only. Held in memory; never written to the keychain or `~/.hereya/secrets/`. Takes precedence over the HEREYA_TOKEN environment variable.',
15
+ required: false,
16
+ }),
17
+ };
10
18
  async run() {
19
+ const { flags } = await this.parse(AppDeployments);
20
+ // Precedence: --token flag > HEREYA_TOKEN env var > keychain.
21
+ if (flags.token) {
22
+ if (process.env.HEREYA_TOKEN) {
23
+ getDefaultLogger().debug('[app deployments] --token flag overrides HEREYA_TOKEN environment variable.');
24
+ }
25
+ return withEphemeralToken(flags.token, () => this.runInner());
26
+ }
27
+ return this.runInner();
28
+ }
29
+ async runInner() {
11
30
  const { args } = await this.parse(AppDeployments);
12
31
  const backend = await getBackend();
13
32
  if (!(backend instanceof CloudBackend)) {
@@ -9,9 +9,11 @@ export default class AppDestroy extends Command {
9
9
  chdir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
10
  local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  parameter: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
12
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  workspace: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
14
  };
14
15
  run(): Promise<void>;
16
+ private runInner;
15
17
  private runLocal;
16
18
  private runRemote;
17
19
  }
@@ -8,6 +8,8 @@ import { getBackend } from '../../../backend/index.js';
8
8
  import { LocalExecutor } from '../../../executor/local.js';
9
9
  import { resolveAndSubstituteAppParams } from '../../../lib/app-parameters.js';
10
10
  import { getEnvManager } from '../../../lib/env/index.js';
11
+ import { withEphemeralToken } from '../../../lib/ephemeral-token.js';
12
+ import { getDefaultLogger } from '../../../lib/log.js';
11
13
  import { arrayOfStringToObject } from '../../../lib/object-utils.js';
12
14
  import { stripOrgPrefix } from '../../../lib/org-utils.js';
13
15
  import { getProfileFromWorkspace } from '../../../lib/profile-utils.js';
@@ -76,6 +78,10 @@ export default class AppDestroy extends Command {
76
78
  description: 'parameter for the app deployment, in the form of key=value (repeatable)',
77
79
  multiple: true,
78
80
  }),
81
+ token: Flags.string({
82
+ description: 'Ephemeral cloud access token used for this invocation only. Held in memory; never written to the keychain or `~/.hereya/secrets/`. Takes precedence over the HEREYA_TOKEN environment variable.',
83
+ required: false,
84
+ }),
79
85
  workspace: Flags.string({
80
86
  char: 'w',
81
87
  description: 'workspace where the app is currently deployed',
@@ -83,6 +89,17 @@ export default class AppDestroy extends Command {
83
89
  }),
84
90
  };
85
91
  async run() {
92
+ const { flags } = await this.parse(AppDestroy);
93
+ // Precedence: --token flag > HEREYA_TOKEN env var > keychain.
94
+ if (flags.token) {
95
+ if (process.env.HEREYA_TOKEN) {
96
+ getDefaultLogger().debug('[app destroy] --token flag overrides HEREYA_TOKEN environment variable.');
97
+ }
98
+ return withEphemeralToken(flags.token, () => this.runInner());
99
+ }
100
+ return this.runInner();
101
+ }
102
+ async runInner() {
86
103
  const { args, flags } = await this.parse(AppDestroy);
87
104
  const forceLocal = flags.local || process.env.HEREYA_LOCAL_EXECUTION === 'true';
88
105
  if (forceLocal) {
@@ -7,7 +7,9 @@ export default class AppEnv extends Command {
7
7
  static description: string;
8
8
  static examples: string[];
9
9
  static flags: {
10
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  workspace: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
12
  };
12
13
  run(): Promise<void>;
14
+ private runInner;
13
15
  }
@@ -1,6 +1,8 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { CloudBackend } from '../../../backend/cloud/cloud-backend.js';
3
3
  import { getBackend } from '../../../backend/index.js';
4
+ import { withEphemeralToken } from '../../../lib/ephemeral-token.js';
5
+ import { getDefaultLogger } from '../../../lib/log.js';
4
6
  export default class AppEnv extends Command {
5
7
  static args = {
6
8
  name: Args.string({ description: 'app name in org/name format', required: true }),
@@ -13,6 +15,10 @@ export default class AppEnv extends Command {
13
15
  '<%= config.bin %> <%= command.id %> my-org/my-app -w my-workspace DATABASE_URL',
14
16
  ];
15
17
  static flags = {
18
+ token: Flags.string({
19
+ description: 'Ephemeral cloud access token used for this invocation only. Held in memory; never written to the keychain or `~/.hereya/secrets/`. Takes precedence over the HEREYA_TOKEN environment variable.',
20
+ required: false,
21
+ }),
16
22
  workspace: Flags.string({
17
23
  char: 'w',
18
24
  description: 'workspace to read env outputs from',
@@ -20,6 +26,17 @@ export default class AppEnv extends Command {
20
26
  }),
21
27
  };
22
28
  async run() {
29
+ const { flags } = await this.parse(AppEnv);
30
+ // Precedence: --token flag > HEREYA_TOKEN env var > keychain.
31
+ if (flags.token) {
32
+ if (process.env.HEREYA_TOKEN) {
33
+ getDefaultLogger().debug('[app env] --token flag overrides HEREYA_TOKEN environment variable.');
34
+ }
35
+ return withEphemeralToken(flags.token, () => this.runInner());
36
+ }
37
+ return this.runInner();
38
+ }
39
+ async runInner() {
23
40
  const { args, flags } = await this.parse(AppEnv);
24
41
  const backend = await getBackend();
25
42
  if (!(backend instanceof CloudBackend)) {
@@ -2,5 +2,9 @@ import { Command } from '@oclif/core';
2
2
  export default class AppList extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
+ static flags: {
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ };
5
8
  run(): Promise<void>;
9
+ private runInner;
6
10
  }
@@ -1,10 +1,29 @@
1
- import { Command } from '@oclif/core';
1
+ import { Command, Flags } from '@oclif/core';
2
2
  import { CloudBackend } from '../../../backend/cloud/cloud-backend.js';
3
3
  import { getBackend } from '../../../backend/index.js';
4
+ import { withEphemeralToken } from '../../../lib/ephemeral-token.js';
5
+ import { getDefaultLogger } from '../../../lib/log.js';
4
6
  export default class AppList extends Command {
5
7
  static description = 'List hereya-apps available to your account.';
6
8
  static examples = ['<%= config.bin %> <%= command.id %>'];
9
+ static flags = {
10
+ token: Flags.string({
11
+ description: 'Ephemeral cloud access token used for this invocation only. Held in memory; never written to the keychain or `~/.hereya/secrets/`. Takes precedence over the HEREYA_TOKEN environment variable.',
12
+ required: false,
13
+ }),
14
+ };
7
15
  async run() {
16
+ const { flags } = await this.parse(AppList);
17
+ // Precedence: --token flag > HEREYA_TOKEN env var > keychain.
18
+ if (flags.token) {
19
+ if (process.env.HEREYA_TOKEN) {
20
+ getDefaultLogger().debug('[app list] --token flag overrides HEREYA_TOKEN environment variable.');
21
+ }
22
+ return withEphemeralToken(flags.token, () => this.runInner());
23
+ }
24
+ return this.runInner();
25
+ }
26
+ async runInner() {
8
27
  const backend = await getBackend();
9
28
  if (!(backend instanceof CloudBackend)) {
10
29
  this.error('Listing apps requires the cloud backend. Run `hereya login` first.');
@@ -6,7 +6,9 @@ export default class AppStatus extends Command {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  workspace: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
11
  };
11
12
  run(): Promise<void>;
13
+ private runInner;
12
14
  }
@@ -1,6 +1,8 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { CloudBackend } from '../../../backend/cloud/cloud-backend.js';
3
3
  import { getBackend } from '../../../backend/index.js';
4
+ import { withEphemeralToken } from '../../../lib/ephemeral-token.js';
5
+ import { getDefaultLogger } from '../../../lib/log.js';
4
6
  export default class AppStatus extends Command {
5
7
  static args = {
6
8
  name: Args.string({ description: 'app name in org/name format', required: true }),
@@ -8,6 +10,10 @@ export default class AppStatus extends Command {
8
10
  static description = 'Show the deployment status of a hereya-app on a workspace.';
9
11
  static examples = ['<%= config.bin %> <%= command.id %> my-org/my-app -w my-workspace'];
10
12
  static flags = {
13
+ token: Flags.string({
14
+ description: 'Ephemeral cloud access token used for this invocation only. Held in memory; never written to the keychain or `~/.hereya/secrets/`. Takes precedence over the HEREYA_TOKEN environment variable.',
15
+ required: false,
16
+ }),
11
17
  workspace: Flags.string({
12
18
  char: 'w',
13
19
  description: 'workspace to read deployment status from',
@@ -15,6 +21,17 @@ export default class AppStatus extends Command {
15
21
  }),
16
22
  };
17
23
  async run() {
24
+ const { flags } = await this.parse(AppStatus);
25
+ // Precedence: --token flag > HEREYA_TOKEN env var > keychain.
26
+ if (flags.token) {
27
+ if (process.env.HEREYA_TOKEN) {
28
+ getDefaultLogger().debug('[app status] --token flag overrides HEREYA_TOKEN environment variable.');
29
+ }
30
+ return withEphemeralToken(flags.token, () => this.runInner());
31
+ }
32
+ return this.runInner();
33
+ }
34
+ async runInner() {
18
35
  const { args, flags } = await this.parse(AppStatus);
19
36
  const backend = await getBackend();
20
37
  if (!(backend instanceof CloudBackend)) {
@@ -6,8 +6,10 @@ export default class Deploy extends Command {
6
6
  chdir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
7
  debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
8
  local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  workspace: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
11
  };
11
12
  run(): Promise<void>;
12
13
  private deployRemotely;
14
+ private runInner;
13
15
  }
@@ -7,8 +7,9 @@ import { getBackend } from '../../backend/index.js';
7
7
  import { getExecutor } from '../../executor/index.js';
8
8
  import { getConfigManager } from '../../lib/config/index.js';
9
9
  import { getEnvManager } from '../../lib/env/index.js';
10
+ import { getEphemeralToken, withEphemeralToken } from '../../lib/ephemeral-token.js';
10
11
  import { gitUtils } from '../../lib/git-utils.js';
11
- import { getLogger, getLogPath, isDebug, setDebug } from '../../lib/log.js';
12
+ import { getDefaultLogger, getLogger, getLogPath, isDebug, setDebug } from '../../lib/log.js';
12
13
  import { getParameterManager } from '../../lib/parameter/index.js';
13
14
  import { getProfileFromWorkspace } from '../../lib/profile-utils.js';
14
15
  import { pollExecutorJob } from '../../lib/remote-job-utils.js';
@@ -35,6 +36,10 @@ export default class Deploy extends Command {
35
36
  default: false,
36
37
  description: 'force local execution (skip remote executor)',
37
38
  }),
39
+ token: Flags.string({
40
+ description: 'Ephemeral cloud access token used for this invocation only. Held in memory; never written to the keychain or `~/.hereya/secrets/`. Takes precedence over the HEREYA_TOKEN environment variable.',
41
+ required: false,
42
+ }),
38
43
  workspace: Flags.string({
39
44
  char: 'w',
40
45
  description: 'name of the workspace to deploy the packages for',
@@ -42,6 +47,90 @@ export default class Deploy extends Command {
42
47
  }),
43
48
  };
44
49
  async run() {
50
+ const { flags } = await this.parse(Deploy);
51
+ // Precedence: --token flag > HEREYA_TOKEN env var > keychain.
52
+ if (flags.token) {
53
+ if (process.env.HEREYA_TOKEN) {
54
+ getDefaultLogger().debug('[deploy] --token flag overrides HEREYA_TOKEN environment variable.');
55
+ }
56
+ return withEphemeralToken(flags.token, () => this.runInner());
57
+ }
58
+ return this.runInner();
59
+ }
60
+ async deployRemotely(input) {
61
+ const myLogger = new ListrLogger({ useIcons: false });
62
+ // 1. Ensure clean working tree
63
+ const cleanCheck = await gitUtils.ensureCleanAndPushed({ cwd: input.projectRootDir });
64
+ if (!cleanCheck.clean) {
65
+ this.error(cleanCheck.reason);
66
+ }
67
+ myLogger.log(ListrLogLevels.STARTED, `Deploying to ${input.workspace} via remote executor (branch: ${input.gitBranch})`);
68
+ // 2. Get cloud backend for job submission. If we are inside an ephemeral
69
+ // token scope (set by --token), use that token directly without touching
70
+ // the keychain or persisted credentials. Otherwise fall back to the
71
+ // standard "load credentials from disk + keychain" path.
72
+ const ephemeral = getEphemeralToken();
73
+ const resolvedWorkspaceName = resolveWorkspaceName(input.workspace, input.project);
74
+ let cloudBackend;
75
+ if (ephemeral) {
76
+ const url = ephemeral.cloudUrl
77
+ ?? process.env.HEREYA_CLOUD_URL
78
+ ?? (await loadBackendConfig()).cloud?.url
79
+ ?? 'https://cloud.hereya.dev';
80
+ cloudBackend = new CloudBackend({
81
+ accessToken: ephemeral.token,
82
+ clientId: '',
83
+ refreshToken: '',
84
+ url,
85
+ });
86
+ }
87
+ else {
88
+ const backendConfig = await loadBackendConfig();
89
+ if (!backendConfig.cloud) {
90
+ this.error('Remote deployment requires cloud backend. Run `hereya login` first.');
91
+ }
92
+ const credentials = await getCloudCredentials(backendConfig.cloud.clientId);
93
+ if (!credentials) {
94
+ this.error('Cloud credentials not found. Run `hereya login` first.');
95
+ }
96
+ cloudBackend = new CloudBackend({
97
+ accessToken: credentials.accessToken,
98
+ clientId: backendConfig.cloud.clientId,
99
+ refreshToken: credentials.refreshToken,
100
+ url: backendConfig.cloud.url,
101
+ });
102
+ }
103
+ // 3. Submit deploy job
104
+ const submitResult = await cloudBackend.submitExecutorJob({
105
+ payload: {
106
+ gitBranch: input.gitBranch,
107
+ project: input.project,
108
+ workspace: input.workspace,
109
+ },
110
+ type: 'deploy',
111
+ workspace: resolvedWorkspaceName,
112
+ });
113
+ if (!submitResult.success) {
114
+ this.error(`Failed to submit remote deploy job: ${submitResult.reason}`);
115
+ }
116
+ // 4. Poll for results
117
+ const logger = {
118
+ debug: (msg) => myLogger.log(ListrLogLevels.OUTPUT, msg),
119
+ error: (msg) => myLogger.log(ListrLogLevels.OUTPUT, msg),
120
+ info: (msg) => myLogger.log(ListrLogLevels.OUTPUT, msg),
121
+ };
122
+ const pollResult = await pollExecutorJob({
123
+ cloudBackend,
124
+ jobId: submitResult.jobId,
125
+ logger,
126
+ workspace: resolvedWorkspaceName,
127
+ });
128
+ if (!pollResult.success) {
129
+ this.error(pollResult.reason);
130
+ }
131
+ myLogger.log(ListrLogLevels.COMPLETED, 'Remote deployment completed successfully');
132
+ }
133
+ async runInner() {
45
134
  const { flags } = await this.parse(Deploy);
46
135
  setDebug(flags.debug);
47
136
  const projectRootDir = path.resolve(flags.chdir || process.env.HEREYA_PROJECT_ROOT_DIR || process.cwd());
@@ -454,58 +543,4 @@ export default class Deploy extends Command {
454
543
  See ${getLogPath()} for more details`);
455
544
  }
456
545
  }
457
- async deployRemotely(input) {
458
- const myLogger = new ListrLogger({ useIcons: false });
459
- // 1. Ensure clean working tree
460
- const cleanCheck = await gitUtils.ensureCleanAndPushed({ cwd: input.projectRootDir });
461
- if (!cleanCheck.clean) {
462
- this.error(cleanCheck.reason);
463
- }
464
- myLogger.log(ListrLogLevels.STARTED, `Deploying to ${input.workspace} via remote executor (branch: ${input.gitBranch})`);
465
- // 2. Get cloud backend for job submission
466
- const backendConfig = await loadBackendConfig();
467
- if (!backendConfig.cloud) {
468
- this.error('Remote deployment requires cloud backend. Run `hereya login` first.');
469
- }
470
- const credentials = await getCloudCredentials(backendConfig.cloud.clientId);
471
- if (!credentials) {
472
- this.error('Cloud credentials not found. Run `hereya login` first.');
473
- }
474
- const resolvedWorkspaceName = resolveWorkspaceName(input.workspace, input.project);
475
- const cloudBackend = new CloudBackend({
476
- accessToken: credentials.accessToken,
477
- clientId: backendConfig.cloud.clientId,
478
- refreshToken: credentials.refreshToken,
479
- url: backendConfig.cloud.url,
480
- });
481
- // 3. Submit deploy job
482
- const submitResult = await cloudBackend.submitExecutorJob({
483
- payload: {
484
- gitBranch: input.gitBranch,
485
- project: input.project,
486
- workspace: input.workspace,
487
- },
488
- type: 'deploy',
489
- workspace: resolvedWorkspaceName,
490
- });
491
- if (!submitResult.success) {
492
- this.error(`Failed to submit remote deploy job: ${submitResult.reason}`);
493
- }
494
- // 4. Poll for results
495
- const logger = {
496
- debug: (msg) => myLogger.log(ListrLogLevels.OUTPUT, msg),
497
- error: (msg) => myLogger.log(ListrLogLevels.OUTPUT, msg),
498
- info: (msg) => myLogger.log(ListrLogLevels.OUTPUT, msg),
499
- };
500
- const pollResult = await pollExecutorJob({
501
- cloudBackend,
502
- jobId: submitResult.jobId,
503
- logger,
504
- workspace: resolvedWorkspaceName,
505
- });
506
- if (!pollResult.success) {
507
- this.error(pollResult.reason);
508
- }
509
- myLogger.log(ListrLogLevels.COMPLETED, 'Remote deployment completed successfully');
510
- }
511
546
  }
@@ -10,7 +10,9 @@ export default class Init extends Command {
10
10
  'deploy-workspace': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  parameter: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
12
12
  template: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  workspace: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
15
  };
15
16
  run(): Promise<void>;
17
+ private runInner;
16
18
  }
@@ -5,9 +5,10 @@ import path from 'node:path';
5
5
  import { getBackend } from '../../backend/index.js';
6
6
  import { getExecutorForWorkspace } from '../../executor/context.js';
7
7
  import { getConfigManager } from '../../lib/config/index.js';
8
+ import { withEphemeralToken } from '../../lib/ephemeral-token.js';
8
9
  import { gitUtils } from '../../lib/git-utils.js';
9
10
  import { hereyaTokenUtils } from '../../lib/hereya-token.js';
10
- import { getLogger, getLogPath } from '../../lib/log.js';
11
+ import { getDefaultLogger, getLogger, getLogPath } from '../../lib/log.js';
11
12
  import { arrayOfStringToObject } from '../../lib/object-utils.js';
12
13
  export default class Init extends Command {
13
14
  static args = {
@@ -41,6 +42,10 @@ export default class Init extends Command {
41
42
  description: 'template package name (e.g. owner/repo)',
42
43
  required: false,
43
44
  }),
45
+ token: Flags.string({
46
+ description: 'Ephemeral cloud access token used for this invocation only. Held in memory; never written to the keychain or `~/.hereya/secrets/`. Takes precedence over the HEREYA_TOKEN environment variable.',
47
+ required: false,
48
+ }),
44
49
  workspace: Flags.string({
45
50
  char: 'w',
46
51
  description: 'workspace to set as default',
@@ -48,6 +53,18 @@ export default class Init extends Command {
48
53
  }),
49
54
  };
50
55
  async run() {
56
+ const { flags } = await this.parse(Init);
57
+ // Precedence: --token flag > HEREYA_TOKEN env var > keychain.
58
+ // The flag wins; emit a debug log if both are present.
59
+ if (flags.token) {
60
+ if (process.env.HEREYA_TOKEN) {
61
+ getDefaultLogger().debug('[init] --token flag overrides HEREYA_TOKEN environment variable.');
62
+ }
63
+ return withEphemeralToken(flags.token, () => this.runInner());
64
+ }
65
+ return this.runInner();
66
+ }
67
+ async runInner() {
51
68
  const { args, flags } = await this.parse(Init);
52
69
  const projectRootDir = flags.chdir || process.env.HEREYA_PROJECT_ROOT_DIR;
53
70
  // Template flow
@@ -17,6 +17,11 @@ export default class Login extends Command {
17
17
  '$ hereya login --token=your-token https://cloud.hereya.dev',
18
18
  ];
19
19
  static flags = {
20
+ // NOTE: `login --token` intentionally exchanges the supplied token for a
21
+ // long-lived refresh token via `loginWithToken` and PERSISTS the result
22
+ // to the keychain / `~/.hereya/secrets/`. This is the right behavior for
23
+ // an explicit `login` step. Other commands accept a `--token` flag with
24
+ // ephemeral, in-memory-only semantics (see `withEphemeralToken`).
20
25
  token: Flags.string({
21
26
  char: 't',
22
27
  description: 'Token to use for login',
@@ -19,6 +19,7 @@ export default class Publish extends Command {
19
19
  static examples: string[];
20
20
  static flags: {
21
21
  chdir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
23
  };
23
24
  calculateGitArchiveSha256(packageDir: string): Promise<string>;
24
25
  displayPublishError(reason: string, config: HereyarcConfig): void;
@@ -30,6 +31,7 @@ export default class Publish extends Command {
30
31
  loadConfig(packageDir: string): Promise<HereyarcConfig>;
31
32
  loadReadme(packageDir: string): string | undefined;
32
33
  run(): Promise<void>;
34
+ runInner(): Promise<void>;
33
35
  validateAppParameters(rawParameters: unknown): Record<string, IParameterSpec>;
34
36
  validateConfig(config: HereyarcConfig): void;
35
37
  private publishApp;
@@ -5,6 +5,8 @@ import { simpleGit } from 'simple-git';
5
5
  import * as yaml from 'yaml';
6
6
  import { CloudBackend } from '../../backend/cloud/cloud-backend.js';
7
7
  import { getBackend } from '../../backend/index.js';
8
+ import { withEphemeralToken } from '../../lib/ephemeral-token.js';
9
+ import { getDefaultLogger } from '../../lib/log.js';
8
10
  import { PARAMETER_NAME_PATTERN, ParameterSpec } from '../../lib/package/index.js';
9
11
  export default class Publish extends Command {
10
12
  static description = 'Publish a package to the Hereya registry';
@@ -20,6 +22,10 @@ export default class Publish extends Command {
20
22
  `,
21
23
  required: false,
22
24
  }),
25
+ token: Flags.string({
26
+ description: 'Ephemeral cloud access token used for this invocation only. Held in memory; never written to the keychain or `~/.hereya/secrets/`. Takes precedence over the HEREYA_TOKEN environment variable.',
27
+ required: false,
28
+ }),
23
29
  };
24
30
  async calculateGitArchiveSha256(packageDir) {
25
31
  const { exec } = await import('node:child_process');
@@ -162,6 +168,17 @@ export default class Publish extends Command {
162
168
  return undefined;
163
169
  }
164
170
  async run() {
171
+ const { flags } = await this.parse(Publish);
172
+ // Precedence: --token flag > HEREYA_TOKEN env var > keychain.
173
+ if (flags.token) {
174
+ if (process.env.HEREYA_TOKEN) {
175
+ getDefaultLogger().debug('[publish] --token flag overrides HEREYA_TOKEN environment variable.');
176
+ }
177
+ return withEphemeralToken(flags.token, () => this.runInner());
178
+ }
179
+ return this.runInner();
180
+ }
181
+ async runInner() {
165
182
  const { flags } = await this.parse(Publish);
166
183
  const packageDir = flags.chdir || process.cwd();
167
184
  try {
@@ -7,8 +7,10 @@ export default class Run extends Command {
7
7
  static examples: string[];
8
8
  static flags: {
9
9
  chdir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  };
12
13
  static strict: boolean;
13
14
  run(): Promise<void>;
15
+ private runInner;
14
16
  }