@credibledata/cred-cli 0.0.105 → 0.0.106
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/README.md +12 -4
- package/dist/agent-contract.spec.js +21 -6
- package/dist/agent-contract.spec.js.map +1 -1
- package/dist/client/api.js +454 -66
- package/dist/client/api.js.map +1 -1
- package/dist/client-codeassist/api.js.map +1 -1
- package/dist/commands/misc/mcp.boot.harness.js +14 -0
- package/dist/commands/misc/mcp.boot.harness.js.map +1 -0
- package/dist/commands/misc/mcp.harness.js +9 -0
- package/dist/commands/misc/mcp.harness.js.map +1 -0
- package/dist/commands/misc/mcp.js +258 -0
- package/dist/commands/misc/mcp.js.map +1 -0
- package/dist/commands/misc/mcp.spec.js +213 -0
- package/dist/commands/misc/mcp.spec.js.map +1 -0
- package/dist/commands/misc/setup.js +72 -3
- package/dist/commands/misc/setup.js.map +1 -1
- package/dist/commands/misc/setup.spec.js +51 -6
- package/dist/commands/misc/setup.spec.js.map +1 -1
- package/dist/commands/misc/status.js +7 -1
- package/dist/commands/misc/status.js.map +1 -1
- package/dist/commands/misc/status.spec.js +34 -0
- package/dist/commands/misc/status.spec.js.map +1 -1
- package/dist/index.js +52 -11
- package/dist/index.js.map +1 -1
- package/dist/setup-core/briefing.d.ts +68 -0
- package/dist/setup-core/briefing.js +20 -11
- package/dist/setup-core/briefing.js.map +1 -1
- package/dist/setup-core/credstore.d.ts +229 -0
- package/dist/setup-core/environments.d.ts +112 -0
- package/dist/setup-core/flow.d.ts +171 -0
- package/dist/setup-core/flow.js +54 -28
- package/dist/setup-core/flow.js.map +1 -1
- package/dist/setup-core/flow.spec.js +135 -54
- package/dist/setup-core/flow.spec.js.map +1 -1
- package/dist/setup-core/folderconfig.d.ts +184 -0
- package/dist/setup-core/folderconfig.js +82 -21
- package/dist/setup-core/folderconfig.js.map +1 -1
- package/dist/setup-core/git.d.ts +57 -0
- package/dist/setup-core/git.js +37 -0
- package/dist/setup-core/git.js.map +1 -1
- package/dist/setup-core/host.d.ts +121 -0
- package/dist/setup-core/importconnections.d.ts +171 -0
- package/dist/setup-core/liveness.d.ts +17 -0
- package/dist/setup-core/localpublisherconfig.d.ts +10 -0
- package/dist/setup-core/mcpconfig.d.ts +132 -0
- package/dist/setup-core/mcpconfig.js +41 -17
- package/dist/setup-core/mcpconfig.js.map +1 -1
- package/dist/setup-core/mcpconfig.spec.js +17 -6
- package/dist/setup-core/mcpconfig.spec.js.map +1 -1
- package/dist/setup-core/packagemanifest.d.ts +28 -0
- package/dist/setup-core/ports.d.ts +95 -0
- package/dist/setup-core/ports.js +6 -5
- package/dist/setup-core/ports.js.map +1 -1
- package/dist/setup-core/publisherconfig.d.ts +163 -0
- package/dist/setup-core/publisherversion.d.ts +22 -0
- package/dist/setup-core/publisherversion.js +1 -1
- package/dist/setup-core/readylines.d.ts +109 -0
- package/dist/setup-core/refresh.d.ts +80 -0
- package/dist/setup-core/skills.d.ts +114 -0
- package/dist/setup-core/skills.js +34 -11
- package/dist/setup-core/skills.js.map +1 -1
- package/dist/setup-core/skills.spec.js +20 -0
- package/dist/setup-core/skills.spec.js.map +1 -1
- package/dist/setup-core/staleservers.d.ts +27 -0
- package/dist/setup-core/staleservers.js +97 -0
- package/dist/setup-core/staleservers.js.map +1 -0
- package/dist/setup-core/staleservers.spec.js +63 -0
- package/dist/setup-core/staleservers.spec.js.map +1 -0
- package/dist/setup-core/supervisor.d.ts +187 -0
- package/dist/setup-core/supervisor.js +1 -0
- package/dist/setup-core/supervisor.js.map +1 -1
- package/dist/setup-core/writefile.d.ts +27 -0
- package/package.json +4 -3
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { type ResolvedTokens } from "@credibledata/cli-auth";
|
|
2
|
+
export type { ResolvedTokens };
|
|
3
|
+
import { Environment } from "./environments";
|
|
4
|
+
/**
|
|
5
|
+
* The `~/.cred` credential store, shared by the `cred` CLI and the VS Code
|
|
6
|
+
* extension.
|
|
7
|
+
*
|
|
8
|
+
* ## Why token reads are claim-based, not field-based
|
|
9
|
+
*
|
|
10
|
+
* The two tools disagree about what the `accessToken` FIELD holds:
|
|
11
|
+
*
|
|
12
|
+
* - the CLI writes the Auth0 **access token** there (`login.ts`), and sends it
|
|
13
|
+
* as `Bearer` to the control plane;
|
|
14
|
+
* - the extension writes the **id token** there on every refresh
|
|
15
|
+
* (`vscode-ext/src/user/user.ts`), and keeps the access token in
|
|
16
|
+
* `jwtAccessToken`.
|
|
17
|
+
*
|
|
18
|
+
* Whichever tool wrote last decides what the field means, so reading it by name
|
|
19
|
+
* makes the CLI's behaviour depend on a race. Instead every read resolves the
|
|
20
|
+
* token it wants by inspecting the JWT's `aud` claim: the access token is
|
|
21
|
+
* audienced to the Auth0 API, the id token to the client id. Both are known per
|
|
22
|
+
* deployment env, so classification is exact rather than heuristic.
|
|
23
|
+
*
|
|
24
|
+
* This is a PERMANENT contract, not a migration step. The shipped extension
|
|
25
|
+
* keeps writing the old shape on every refresh, so a one-time fixup would be
|
|
26
|
+
* undone within minutes of the user opening VS Code. It can be deleted only
|
|
27
|
+
* once the extension writes the same shape (Phase 2 of the local-devex plan).
|
|
28
|
+
*
|
|
29
|
+
* A schema-version marker was considered and rejected for the same reason: the
|
|
30
|
+
* extension mutates the file after we stamp it, so a version field would claim
|
|
31
|
+
* a shape the file no longer has. Claim-based classification needs no marker
|
|
32
|
+
* because it reads the tokens themselves.
|
|
33
|
+
*
|
|
34
|
+
* Writes stay in the shape the extension already understands — `accessToken`
|
|
35
|
+
* and `jwtAccessToken` both set to the access token, `idToken` added alongside —
|
|
36
|
+
* so nothing about this change alters what the extension sees today.
|
|
37
|
+
*/
|
|
38
|
+
interface StoredEnvConfig {
|
|
39
|
+
organization?: string;
|
|
40
|
+
project?: string;
|
|
41
|
+
auth?: {
|
|
42
|
+
username: string;
|
|
43
|
+
password: string;
|
|
44
|
+
};
|
|
45
|
+
vsCodeUser?: {
|
|
46
|
+
username: string;
|
|
47
|
+
email: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Ambiguous by history: the access token when the CLI wrote it, the id token
|
|
51
|
+
* when the extension did. Never read by name — see {@link resolveTokens}.
|
|
52
|
+
*/
|
|
53
|
+
accessToken?: string;
|
|
54
|
+
/** The access token, per the extension's convention. Also written by the CLI. */
|
|
55
|
+
jwtAccessToken?: string;
|
|
56
|
+
/** The id token. Written by this CLI only; absent in files the extension wrote. */
|
|
57
|
+
idToken?: string;
|
|
58
|
+
refreshToken?: string;
|
|
59
|
+
isServiceAccountToken?: boolean;
|
|
60
|
+
}
|
|
61
|
+
interface ConfigFile {
|
|
62
|
+
configPerEnvironment: Partial<Record<Environment, StoredEnvConfig>>;
|
|
63
|
+
activeEnvironment: Environment;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Thrown when `.cred` exists but cannot be used: either it does not parse as
|
|
67
|
+
* JSON or it parses but lacks the required top-level shape. Distinct from fs
|
|
68
|
+
* errors (ENOENT, EACCES) so callers that want to recover — `cred login` —
|
|
69
|
+
* can target *only* a corrupt-content failure and leave permission/IO errors
|
|
70
|
+
* to surface loudly.
|
|
71
|
+
*/
|
|
72
|
+
export declare class InvalidConfigError extends Error {
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Sort the stored token fields into access token and id token by audience,
|
|
76
|
+
* using the rules `cred` and `ms2ad` share (cli-auth/src/index.ts, where each
|
|
77
|
+
* rule is written down and tested), bound to this deployment's Auth0 values.
|
|
78
|
+
*
|
|
79
|
+
* A token it cannot identify comes back `undefined`, so the caller says "run
|
|
80
|
+
* `cred login`" instead of sending a token the control plane will refuse.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveTokens(stored: StoredEnvConfig, environment: Environment): ResolvedTokens;
|
|
83
|
+
/**
|
|
84
|
+
* Where a command run inside a pinned directory acts: the pin's deployment,
|
|
85
|
+
* organization and environment (`--cell` takes precedence over the pin). See {@link ClientConfiguration.scopeCommand}.
|
|
86
|
+
*/
|
|
87
|
+
export interface CommandScope {
|
|
88
|
+
deployment: Environment;
|
|
89
|
+
organization?: string;
|
|
90
|
+
environment?: string;
|
|
91
|
+
}
|
|
92
|
+
export declare class ClientConfiguration {
|
|
93
|
+
/** Set once per process, before any command reads the store. */
|
|
94
|
+
private static commandScope?;
|
|
95
|
+
/**
|
|
96
|
+
* Make every default store (`new ClientConfiguration()`, the form the ~40
|
|
97
|
+
* CLI commands build for themselves) read `scope` instead of the global
|
|
98
|
+
* `activeEnvironment`, organization and environment, without writing them.
|
|
99
|
+
*
|
|
100
|
+
* The CLI calls this once at startup, for a command run in a directory
|
|
101
|
+
* `cred init` pinned, unless `--cell` names another cell: that
|
|
102
|
+
* cell's own store applies, and the pin is ignored. Without it those commands read only `~/.cred`, so in
|
|
103
|
+
* a directory pinned to one organization `cred ls connections` listed
|
|
104
|
+
* another's, and `cred rm connection` would have deleted from it.
|
|
105
|
+
*
|
|
106
|
+
* A store built with an explicit path or deployment is left alone: those
|
|
107
|
+
* are the callers that already chose where to read, and the specs.
|
|
108
|
+
*/
|
|
109
|
+
static scopeCommand(scope: CommandScope | undefined): void;
|
|
110
|
+
private configPath;
|
|
111
|
+
private config;
|
|
112
|
+
private environmentOverride?;
|
|
113
|
+
private scope?;
|
|
114
|
+
/**
|
|
115
|
+
* @param customConfigPath an alternate `~/.cred` location, for tests.
|
|
116
|
+
* @param environmentOverride pin every per-environment read to this
|
|
117
|
+
* deployment instead of the file's `activeEnvironment`, without
|
|
118
|
+
* writing to disk. `~/.cred` stores credentials per deployment, so a
|
|
119
|
+
* caller that targets a deployment other than the active one needs
|
|
120
|
+
* its token, organization and environment from that deployment too:
|
|
121
|
+
* resolving only the URL would talk to one deployment's control plane
|
|
122
|
+
* with another's token.
|
|
123
|
+
*/
|
|
124
|
+
constructor(customConfigPath?: string, environmentOverride?: Environment);
|
|
125
|
+
/**
|
|
126
|
+
* The same file, with every per-deployment read and write pinned to
|
|
127
|
+
* `environment` — the one this store resolves to now. For an operation that
|
|
128
|
+
* reads, waits and then writes: without the pin, the write resolves the
|
|
129
|
+
* deployment again and follows an `activeEnvironment` changed in between.
|
|
130
|
+
*/
|
|
131
|
+
pinnedToCurrentEnvironment(): ClientConfiguration;
|
|
132
|
+
/** The same file, with every per-deployment read and write pinned to `environment`. */
|
|
133
|
+
pinnedTo(environment: Environment): ClientConfiguration;
|
|
134
|
+
/** Where this store lives on disk. The token refresh locks a file beside it. */
|
|
135
|
+
getConfigPath(): string;
|
|
136
|
+
loadConfig(): ConfigFile;
|
|
137
|
+
/**
|
|
138
|
+
* Ensure `.cred` is usable, recovering from a corrupt file if needed.
|
|
139
|
+
*
|
|
140
|
+
* `loadConfig` deliberately fails loud on a corrupt/invalid file so a read
|
|
141
|
+
* path never silently erases tokens. But `cred login` exists precisely to
|
|
142
|
+
* (re)establish config, so it must be able to get past a bad file — that is
|
|
143
|
+
* what the "re-run `cred login` to reset" message promises. We make the
|
|
144
|
+
* message true here: the bad file is renamed aside (never deleted, so a
|
|
145
|
+
* support engineer can still inspect it) and a fresh default is written.
|
|
146
|
+
*
|
|
147
|
+
* Only an {@link InvalidConfigError} triggers recovery. fs errors such as
|
|
148
|
+
* EACCES are real environment problems and are rethrown — resetting would
|
|
149
|
+
* just fail to write anyway, and we must not mask a permissions issue as a
|
|
150
|
+
* successful reset.
|
|
151
|
+
*
|
|
152
|
+
* @returns the path the corrupt file was backed up to, or null if the
|
|
153
|
+
* existing config was already valid. The caller decides how to
|
|
154
|
+
* report it; this module does not own a logger.
|
|
155
|
+
*/
|
|
156
|
+
recoverIfCorrupt(): string | null;
|
|
157
|
+
private defaultConfig;
|
|
158
|
+
private writeConfig;
|
|
159
|
+
private currentEnvConfig;
|
|
160
|
+
setOrganization(organizationName: string): void;
|
|
161
|
+
setProject(projectName: string | undefined): void;
|
|
162
|
+
getOrganization(): string;
|
|
163
|
+
getProject(): string;
|
|
164
|
+
setAccessToken(userToken: boolean, accessToken: string): void;
|
|
165
|
+
/**
|
|
166
|
+
* Persist a freshly-issued Auth0 token pair.
|
|
167
|
+
*
|
|
168
|
+
* Writes the access token to BOTH `accessToken` and `jwtAccessToken`, which
|
|
169
|
+
* is the shape the CLI has always written and the shape the extension can
|
|
170
|
+
* still read (it takes the access token from `jwtAccessToken`). `idToken`
|
|
171
|
+
* gets its own field so it is never confused with the access token again.
|
|
172
|
+
*/
|
|
173
|
+
setTokenPair(tokens: {
|
|
174
|
+
accessToken: string;
|
|
175
|
+
idToken?: string;
|
|
176
|
+
refreshToken?: string;
|
|
177
|
+
}): void;
|
|
178
|
+
/**
|
|
179
|
+
* The token to put in an `Authorization` header, resolved by audience rather
|
|
180
|
+
* than by field name so it is correct whichever tool wrote the file last.
|
|
181
|
+
*/
|
|
182
|
+
getApiAccessToken(): string | undefined;
|
|
183
|
+
getIdToken(): string | undefined;
|
|
184
|
+
getAuthenticationHeader(): string;
|
|
185
|
+
/**
|
|
186
|
+
* @deprecated Ambiguous — returns the raw `accessToken` field, whose meaning
|
|
187
|
+
* depends on which tool wrote it last. Use {@link getApiAccessToken} for an
|
|
188
|
+
* `Authorization` header or {@link getIdToken} for profile claims.
|
|
189
|
+
*/
|
|
190
|
+
getAccessToken(): string;
|
|
191
|
+
setJwtAccessToken(jwtAccessToken: string): void;
|
|
192
|
+
getJwtAccessToken(): string | undefined;
|
|
193
|
+
setVSCodeUser(username: string, email: string): void;
|
|
194
|
+
getVSCodeUser(): {
|
|
195
|
+
username: string;
|
|
196
|
+
email: string;
|
|
197
|
+
} | undefined;
|
|
198
|
+
setRefreshToken(refreshToken: string): void;
|
|
199
|
+
getRefreshToken(): string | undefined;
|
|
200
|
+
isServiceAccountToken(): boolean;
|
|
201
|
+
doesConfigExists(): boolean;
|
|
202
|
+
getHeaders(): {
|
|
203
|
+
Authorization: string;
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* Remove this deployment's tokens and keep the rest of its entry: the
|
|
207
|
+
* organization and environment (`project`) the user chose. For a session
|
|
208
|
+
* Auth0 has refused, where the next step is signing back in to the same
|
|
209
|
+
* place. `cred logout` uses `destroyConfig`, which forgets everything.
|
|
210
|
+
*/
|
|
211
|
+
clearTokens(): void;
|
|
212
|
+
destroyConfig(): void;
|
|
213
|
+
setEnvironment(environment: Environment): void;
|
|
214
|
+
getEnvironment(): Environment;
|
|
215
|
+
/** The `activeEnvironment` the file records, whatever `--cell` or a pin says this store acts on. */
|
|
216
|
+
getGlobalEnvironment(): Environment;
|
|
217
|
+
/** The cells whose slot holds a token, in the order the file lists them. */
|
|
218
|
+
signedInCells(): Environment[];
|
|
219
|
+
/**
|
|
220
|
+
* Seconds until the API access token expires; negative once it has. Returns
|
|
221
|
+
* `undefined` when there is no decodable expiry — a service-account token or
|
|
222
|
+
* a malformed one — so callers can tell "no deadline" from "expires soon".
|
|
223
|
+
*/
|
|
224
|
+
secondsUntilExpiry(): number | undefined;
|
|
225
|
+
isTokenValid(): {
|
|
226
|
+
valid: boolean;
|
|
227
|
+
reason?: string;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment-environment tables: which control plane, which Auth0 tenant, and
|
|
3
|
+
* which cluster each deployment env lives on.
|
|
4
|
+
*
|
|
5
|
+
* These live in setup-core rather than in the CLI's `constants.ts` because both
|
|
6
|
+
* front doors need them: the credential store keys `~/.cred` by deployment env,
|
|
7
|
+
* and token refresh needs the Auth0 domain and client id for the env it is
|
|
8
|
+
* refreshing. `constants.ts` re-exports them, so existing CLI imports are
|
|
9
|
+
* unchanged.
|
|
10
|
+
*
|
|
11
|
+
* DELIBERATELY NO SECRETS HERE. The `e2e-test` machine-to-machine client secret
|
|
12
|
+
* stays in the CLI's `constants.ts`, where its single consumer (`loginM2M`)
|
|
13
|
+
* lives. setup-core is destined to be a published library; a credential in it
|
|
14
|
+
* would be shipped to every consumer of that library.
|
|
15
|
+
*
|
|
16
|
+
* Note the naming collision this file inherits and does not fix: `Environment`
|
|
17
|
+
* here is the DEPLOYMENT env (production/staging/dev/localhost), an internal
|
|
18
|
+
* axis. The user-facing "environment" is the Credible environment inside an
|
|
19
|
+
* organization, which the store calls `project` for legacy reasons.
|
|
20
|
+
*/
|
|
21
|
+
export type Environment = "production" | "staging" | "dev" | "localhost" | "e2e-test" | "videoamp" | "aws-dev" | "aws-dev-2" | "aws-affinity";
|
|
22
|
+
export declare const controlPlanesByEnv: Record<Environment, string>;
|
|
23
|
+
/**
|
|
24
|
+
* The app per deployment, without the organization. Organizations are a
|
|
25
|
+
* subdomain (`<org>.<host>`), as `getOrganizationUrl` builds it in
|
|
26
|
+
* `app/src/components/Utils.ts`; see {@link packageVersionUrl}.
|
|
27
|
+
*/
|
|
28
|
+
export declare const appHostsByEnv: Record<Environment, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Where a published version opens in the app: the `/:project/:package/:version`
|
|
31
|
+
* route in `app/src/AdminApp.tsx`, on the organization's own subdomain.
|
|
32
|
+
*/
|
|
33
|
+
export declare function packageVersionUrl(deployment: Environment, organization: string, environment: string, packageName: string, versionId: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* The code-assist API per deployment, for the skills installer. Keyed off
|
|
36
|
+
* {@link Environment} like the other host tables, so adding a deployment
|
|
37
|
+
* without a host here fails the build rather than falling through to
|
|
38
|
+
* production. Values are the ones the VS Code extension ships in
|
|
39
|
+
* `src/config/public/<env>.json`.
|
|
40
|
+
*/
|
|
41
|
+
export declare const codeAssistByEnv: Record<Environment, string>;
|
|
42
|
+
/** Environments that must have an object in `~/.cred` `configPerEnvironment` (avoids undefined on write). */
|
|
43
|
+
export declare const ALL_ENVIRONMENTS: Environment[];
|
|
44
|
+
/**
|
|
45
|
+
* The deployment env everything falls back to.
|
|
46
|
+
*
|
|
47
|
+
* Named because two places have to agree on it and neither is obviously the
|
|
48
|
+
* authority: it is what a fresh `~/.cred` records as `activeEnvironment`, and it
|
|
49
|
+
* is what a folder pin with no `deployment` means (earlier builds left it out
|
|
50
|
+
* for this value; see `resolveDeployment`).
|
|
51
|
+
*/
|
|
52
|
+
export declare const DEFAULT_DEPLOYMENT: Environment;
|
|
53
|
+
/** Is `value` one of the deployment envs this build knows about? */
|
|
54
|
+
export declare function isEnvironment(value: unknown): value is Environment;
|
|
55
|
+
/**
|
|
56
|
+
* Public Auth0 parameters per deployment env. All three fields are public by
|
|
57
|
+
* construction: `clientId` and `audience` travel in every `/authorize` URL, and
|
|
58
|
+
* `domain` is the tenant hostname.
|
|
59
|
+
*/
|
|
60
|
+
export interface Auth0EnvConfig {
|
|
61
|
+
domain: string;
|
|
62
|
+
clientId: string;
|
|
63
|
+
audience: string;
|
|
64
|
+
}
|
|
65
|
+
export declare const auth0ConfigsByEnv: Record<Environment, Auth0EnvConfig>;
|
|
66
|
+
/** Clusters a CLI org can target via `cred login <org> -c <cluster>`. */
|
|
67
|
+
export type Cluster = "gcp" | "aws";
|
|
68
|
+
/** Which cluster each {@link Environment} lives on. */
|
|
69
|
+
export declare const CLUSTER_BY_ENVIRONMENT: Record<Environment, Cluster>;
|
|
70
|
+
/**
|
|
71
|
+
* Default {@link Environment} to switch to when logging into a cluster and the
|
|
72
|
+
* current active env is on a different cluster. Picked per cluster so that
|
|
73
|
+
* `cred login <org> -c aws` flips to videoamp and `-c gcp` flips to production.
|
|
74
|
+
*/
|
|
75
|
+
export declare const DEFAULT_ENVIRONMENT_BY_CLUSTER: Record<Cluster, Environment>;
|
|
76
|
+
/**
|
|
77
|
+
* Per-org CLI login config. One row per org that needs non-default behavior
|
|
78
|
+
* (multi-cluster support, Auth0 Organizations PKCE, etc.). Orgs not listed here
|
|
79
|
+
* fall back to: gcp cluster only, Device Authorization Flow, no Auth0 org id.
|
|
80
|
+
*
|
|
81
|
+
* Auth0 docs: `organization` is supported on `/authorize` for Organizations
|
|
82
|
+
* (https://auth0.com/docs/manage-users/organizations/configure-organizations/use-org-name-authentication-api)
|
|
83
|
+
* but NOT on `POST /oauth/device/code`
|
|
84
|
+
* (https://auth0.com/docs/get-started/authentication-and-authorization-flow/device-authorization-flow/call-your-api-using-the-device-authorization-flow#device-code-parameters),
|
|
85
|
+
* so orgs with an `auth0OrganizationIds` entry for the active cluster use Auth
|
|
86
|
+
* Code + PKCE via the CLI's loopback redirect URI. Keep aligned with the app's
|
|
87
|
+
* `ORGANIZATION_HOST_ORG_ID_MAP` / `getAuth0OrganizationAuthorizationParams`.
|
|
88
|
+
*/
|
|
89
|
+
export interface CliOrgConfig {
|
|
90
|
+
/** Lowercase CLI org name (what the user types after `cred login`). */
|
|
91
|
+
cliOrgName: string;
|
|
92
|
+
/** Clusters this org can be logged into. */
|
|
93
|
+
supportedClusters: Cluster[];
|
|
94
|
+
/** Cluster used when `-c/--cluster` is omitted. Must be in `supportedClusters`. */
|
|
95
|
+
defaultCluster: Cluster;
|
|
96
|
+
/**
|
|
97
|
+
* Auth0 Organization ID per cluster. Presence triggers the PKCE + `organization=<id>`
|
|
98
|
+
* `/authorize` flow on that cluster; absence falls back to Device Authorization Flow.
|
|
99
|
+
*/
|
|
100
|
+
auth0OrganizationIds?: Partial<Record<Cluster, string>>;
|
|
101
|
+
}
|
|
102
|
+
/** Returns the registry entry for an org, or `undefined` if it uses defaults (gcp + device flow). */
|
|
103
|
+
export declare function getCliOrgConfig(organizationName: string): CliOrgConfig | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Auth0 Organization ID for the given CLI org on the cluster it is logging into.
|
|
106
|
+
* Returning a value selects the Auth Code + PKCE branch in `login()`; `undefined`
|
|
107
|
+
* falls back to Device Authorization Flow.
|
|
108
|
+
*
|
|
109
|
+
* `cluster` defaults to the org's `defaultCluster` (or `gcp` for unregistered orgs)
|
|
110
|
+
* so callers that don't yet know the active cluster keep working.
|
|
111
|
+
*/
|
|
112
|
+
export declare function getAuth0OrganizationIdForCliLogin(organizationName: string, cluster?: Cluster): string | undefined;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { ClientConfiguration } from "./credstore";
|
|
2
|
+
import { Environment } from "./environments";
|
|
3
|
+
import { ConnectionCreator, ConnectionSelection } from "./importconnections";
|
|
4
|
+
import { Host } from "./host";
|
|
5
|
+
import { CredibleConnection, PublisherConfig } from "./publisherconfig";
|
|
6
|
+
import { SkillsFetcher, SkillsPolicy } from "./skills";
|
|
7
|
+
import { RunningPublisher } from "./supervisor";
|
|
8
|
+
/**
|
|
9
|
+
* The setup flow itself: the six steps both front doors run.
|
|
10
|
+
*
|
|
11
|
+
* Nothing here knows whether it is talking to a terminal or to VS Code — that is
|
|
12
|
+
* the {@link Host}'s job — and nothing here parses arguments or prints. The end
|
|
13
|
+
* state on disk is therefore identical whichever front door ran it, which is the
|
|
14
|
+
* point of having one flow rather than two implementations.
|
|
15
|
+
*/
|
|
16
|
+
/** The one thing setup needs from the control plane, injected so it can be faked. */
|
|
17
|
+
export type ConnectionLister = (organization: string, credibleEnvironment: string) => Promise<CredibleConnection[]>;
|
|
18
|
+
export interface FlowContext {
|
|
19
|
+
host: Host;
|
|
20
|
+
credStore: ClientConfiguration;
|
|
21
|
+
listConnections: ConnectionLister;
|
|
22
|
+
/**
|
|
23
|
+
* Create a connection in Credible, or test one with `dryRun`.
|
|
24
|
+
*
|
|
25
|
+
* Optional: a front door that cannot create connections simply does not offer
|
|
26
|
+
* the import, rather than offering it and failing at the last step.
|
|
27
|
+
*/
|
|
28
|
+
createConnection?: ConnectionCreator;
|
|
29
|
+
/**
|
|
30
|
+
* Fetch the modeling skills from code-assist.
|
|
31
|
+
*
|
|
32
|
+
* Optional for the same reason as `createConnection`: a front door without
|
|
33
|
+
* access to that API skips the step instead of offering it and failing.
|
|
34
|
+
*/
|
|
35
|
+
fetchSkills?: SkillsFetcher;
|
|
36
|
+
deployment: Environment;
|
|
37
|
+
publisherVersion: string;
|
|
38
|
+
cwd: string;
|
|
39
|
+
/**
|
|
40
|
+
* Stream all of the server's output to the terminal, as `--debug` asks.
|
|
41
|
+
* Otherwise only its errors are shown, and the rest goes to its log file.
|
|
42
|
+
*/
|
|
43
|
+
showAllPublisherOutput?: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface SetupOptions {
|
|
46
|
+
/** Wire this directory instead of the one setup was run in. */
|
|
47
|
+
directory?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Which organization and Credible environment to wire this directory to.
|
|
50
|
+
*
|
|
51
|
+
* Passed in, never read from the credential store here. The store holds one
|
|
52
|
+
* organization and environment per *deployment*, so a flow that read them
|
|
53
|
+
* itself could only ever wire every directory to the same pair — which is
|
|
54
|
+
* exactly what it used to do, leaving the directory's own pin decorative.
|
|
55
|
+
* Resolving them is the front door's job (`resolveIdentity`), so the
|
|
56
|
+
* precedence between a flag, the pin and the global value is decided once.
|
|
57
|
+
*/
|
|
58
|
+
organization: string;
|
|
59
|
+
credibleEnvironment: string;
|
|
60
|
+
/**
|
|
61
|
+
* Do not offer to adopt a local `publisher.config.json`.
|
|
62
|
+
*
|
|
63
|
+
* `cred dev` sets this: the offer belongs to the once-per-directory command,
|
|
64
|
+
* and re-asking every session would be nagging about a decision already made.
|
|
65
|
+
*/
|
|
66
|
+
skipConnectionImport?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Which local connections to import, when already decided (`--connections`).
|
|
69
|
+
* Absent means ask, which a host without a terminal refuses with the flag.
|
|
70
|
+
*/
|
|
71
|
+
connections?: ConnectionSelection;
|
|
72
|
+
/**
|
|
73
|
+
* What to do with skills already in `.claude/skills/` that setup did not
|
|
74
|
+
* install (`--skills`, on `init` and `dev`). Absent means the pin's earlier
|
|
75
|
+
* `none`, else ask — only on a first install that finds some; later runs
|
|
76
|
+
* keep whatever is there.
|
|
77
|
+
*/
|
|
78
|
+
skills?: SkillsPolicy;
|
|
79
|
+
/**
|
|
80
|
+
* Which listed package to serve, relative to the directory (`--package`),
|
|
81
|
+
* in a workspace whose `publisher.config.json` lists several. Absent means
|
|
82
|
+
* the pin's choice, else the only one, else ask.
|
|
83
|
+
*/
|
|
84
|
+
package?: string;
|
|
85
|
+
/**
|
|
86
|
+
* The UI port to serve on (`--port`); MCP takes the port 40 above it.
|
|
87
|
+
* Remembered, and it wins over the remembered pair. Absent means the
|
|
88
|
+
* remembered pair, else 4000/4040, else one derived from the directory.
|
|
89
|
+
*/
|
|
90
|
+
port?: number;
|
|
91
|
+
/** Write the files but do not start a server. */
|
|
92
|
+
start: boolean;
|
|
93
|
+
/** Start a server but do not open a browser. */
|
|
94
|
+
open: boolean;
|
|
95
|
+
}
|
|
96
|
+
export interface WiredDirectory {
|
|
97
|
+
/** The workspace: where `.mcp.json`, the briefing, the skills and the pin go. */
|
|
98
|
+
directory: string;
|
|
99
|
+
/**
|
|
100
|
+
* The package the publisher serves. Usually `directory`; for a Publisher
|
|
101
|
+
* server directory (a `publisher.config.json` listing packages, as
|
|
102
|
+
* `create-malloy-package` lays out), the package it lists.
|
|
103
|
+
*/
|
|
104
|
+
packageDir: string;
|
|
105
|
+
organization: string;
|
|
106
|
+
credibleEnvironment: string;
|
|
107
|
+
config: PublisherConfig;
|
|
108
|
+
configPath: string;
|
|
109
|
+
/** Where the server keeps its own state. Not the user's directory — see below. */
|
|
110
|
+
serverRoot: string;
|
|
111
|
+
uiPort: number;
|
|
112
|
+
mcpPort: number;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Is this directory's own session running — not merely something answering on
|
|
116
|
+
* its pinned port?
|
|
117
|
+
*
|
|
118
|
+
* All three must hold: the session's stamped config and pid file exist (the
|
|
119
|
+
* supervisor removes both when the server exits), the pid is alive, and the
|
|
120
|
+
* port answers. The files alone were not enough: a `kill -9`, a reboot or a
|
|
121
|
+
* closed terminal leaves them behind, and two directories wired one after the
|
|
122
|
+
* other both pin 4000/4040, so the other directory's publisher answered and
|
|
123
|
+
* this one was reported as running.
|
|
124
|
+
*/
|
|
125
|
+
export declare function isOwnSessionRunning(directory: string, uiPort: number): Promise<boolean>;
|
|
126
|
+
/** True when `file` is older than this machine's last boot. */
|
|
127
|
+
export declare function writtenBeforeBoot(file: string, now?: number): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Refuse, before anything is written, the questions `cred init` could not ask.
|
|
130
|
+
*
|
|
131
|
+
* Without a terminal a question exits 2. Asked in order, the connections and
|
|
132
|
+
* skills questions came after the environment was created and selected, so an
|
|
133
|
+
* agent that stopped at the exit 2 left an empty environment behind and
|
|
134
|
+
* `~/.cred` pointing at it. Checked up front, exit 2 means nothing happened,
|
|
135
|
+
* which is what a caller re-running with the flag assumes. A misspelled
|
|
136
|
+
* `--connections` name is refused here for the same reason.
|
|
137
|
+
*/
|
|
138
|
+
export declare function refuseUnanswerable(directory: string, options: {
|
|
139
|
+
connections?: ConnectionSelection;
|
|
140
|
+
skills?: SkillsPolicy;
|
|
141
|
+
package?: string;
|
|
142
|
+
}, interactive: boolean): void;
|
|
143
|
+
/**
|
|
144
|
+
* Refuse to start beside someone else's publisher, before login and the
|
|
145
|
+
* environment lookup. Checked only inside `wireDirectory`, the refusal came
|
|
146
|
+
* after both, 25 seconds and more on a slow deployment.
|
|
147
|
+
*/
|
|
148
|
+
export declare function refuseBesideOtherPublisher(directory: string, port?: number): Promise<void>;
|
|
149
|
+
export declare function wireDirectory(context: FlowContext, options: SetupOptions): Promise<WiredDirectory>;
|
|
150
|
+
/** The flag that answers {@link resolvePackageDir}'s question. */
|
|
151
|
+
export declare const PACKAGE_FLAG = "--package";
|
|
152
|
+
export declare const PORT_FLAG = "--port";
|
|
153
|
+
/** Step 5 and 6: start the server, and open it once it is genuinely serving. */
|
|
154
|
+
export declare function startSession(context: FlowContext, wired: WiredDirectory, options: {
|
|
155
|
+
open: boolean;
|
|
156
|
+
}): Promise<RunningPublisher>;
|
|
157
|
+
/**
|
|
158
|
+
* The whole flow: wire, then run.
|
|
159
|
+
*
|
|
160
|
+
* Setup deliberately ends in a running server rather than a printed
|
|
161
|
+
* instruction. Every step before that is invisible plumbing the user cannot
|
|
162
|
+
* check, and the Publisher UI is the first artifact that proves the whole chain
|
|
163
|
+
* — auth, connections, proxy, compile — is actually live. A setup that ends with
|
|
164
|
+
* "now run `cred dev`" ends on an unverified claim.
|
|
165
|
+
*/
|
|
166
|
+
export declare function runSetup(context: FlowContext, options: SetupOptions): Promise<{
|
|
167
|
+
wired: WiredDirectory;
|
|
168
|
+
running?: RunningPublisher;
|
|
169
|
+
}>;
|
|
170
|
+
/** "a", "a and b", "a, b and c". */
|
|
171
|
+
export declare function joinNames(names: string[]): string;
|
package/dist/setup-core/flow.js
CHANGED
|
@@ -226,14 +226,14 @@ function otherPublisherError(free) {
|
|
|
226
226
|
/** The flag that moves this directory to a free pair, with the pair to pass. */
|
|
227
227
|
function portFix(free) {
|
|
228
228
|
return free
|
|
229
|
-
? `re-run with ${exports.PORT_FLAG} ${free.uiPort} (free now; MCP takes ${free.mcpPort}). cred remembers it
|
|
230
|
-
: `re-run with ${exports.PORT_FLAG} <port>, a port that is free along with the one 40 above it. cred remembers it
|
|
229
|
+
? `re-run with ${exports.PORT_FLAG} ${free.uiPort} (free now; MCP takes ${free.mcpPort}). cred remembers it.`
|
|
230
|
+
: `re-run with ${exports.PORT_FLAG} <port>, a port that is free along with the one 40 above it. cred remembers it.`;
|
|
231
231
|
}
|
|
232
232
|
/**
|
|
233
233
|
* Whether someone else's publisher holds the default pair this directory would
|
|
234
234
|
* use: in the "I already run a publisher" journey, the user's own, started from
|
|
235
|
-
* this same directory. Moving to a derived pair
|
|
236
|
-
*
|
|
235
|
+
* this same directory. Moving to a derived pair left the agent on a port
|
|
236
|
+
* nothing served until `cred dev`. Not this directory's own session; the
|
|
237
237
|
* caller checks that first.
|
|
238
238
|
*/
|
|
239
239
|
function otherPublisherOnDefaults(directory, pinnedPorts) {
|
|
@@ -301,16 +301,15 @@ function wireDirectory(context, options) {
|
|
|
301
301
|
// port pair, and compared against `findFolderConfig`'s always-absolute
|
|
302
302
|
// `root` to find the ports this directory used last. A relative
|
|
303
303
|
// `--directory` reached all three as a different string, so `cred init -d
|
|
304
|
-
// sub` re-derived its ports every run
|
|
305
|
-
//
|
|
306
|
-
// treat it as absolute.
|
|
304
|
+
// sub` re-derived its ports every run, so the Publisher's address moved
|
|
305
|
+
// between runs. Everything downstream can then treat it as absolute.
|
|
307
306
|
const directory = path_1.default.resolve((_a = options.directory) !== null && _a !== void 0 ? _a : context.cwd);
|
|
308
307
|
const remembered = (0, folderconfig_1.findFolderConfig)(directory);
|
|
309
308
|
const pinned = (remembered === null || remembered === void 0 ? void 0 : remembered.root) === directory ? remembered.config : undefined;
|
|
310
309
|
// A second session for the same directory would find the pinned ports busy
|
|
311
|
-
// — held by the first — fall through to another pair,
|
|
312
|
-
//
|
|
313
|
-
//
|
|
310
|
+
// — held by the first — fall through to another pair, move the pin to it
|
|
311
|
+
// and start a second server, leaving the first serving where any open
|
|
312
|
+
// agent's `cred mcp` is connected.
|
|
314
313
|
const ownSessionRunning = Boolean((pinned === null || pinned === void 0 ? void 0 : pinned.ports) &&
|
|
315
314
|
(yield isOwnSessionRunning(directory, pinned.ports.uiPort)));
|
|
316
315
|
if (options.start && ownSessionRunning) {
|
|
@@ -318,9 +317,9 @@ function wireDirectory(context, options) {
|
|
|
318
317
|
}
|
|
319
318
|
// Someone else's publisher on the default pair — in the "I already run a
|
|
320
319
|
// publisher" journey, the user's own, started from this same directory.
|
|
321
|
-
// Moving to a derived pair
|
|
322
|
-
//
|
|
323
|
-
//
|
|
320
|
+
// Moving to a derived pair left the agent on a port nothing served until
|
|
321
|
+
// `cred dev`. Staying on the default pair keeps the address; the old server
|
|
322
|
+
// has to stop first.
|
|
324
323
|
const otherPublisher = options.port === undefined &&
|
|
325
324
|
!ownSessionRunning &&
|
|
326
325
|
(yield otherPublisherOnDefaults(directory, pinned === null || pinned === void 0 ? void 0 : pinned.ports));
|
|
@@ -388,11 +387,11 @@ function wireDirectory(context, options) {
|
|
|
388
387
|
environmentName: (0, publisherconfig_1.localEnvironmentName)(credibleEnvironment),
|
|
389
388
|
retrieval: retrieval && "settings" in retrieval ? retrieval.settings : undefined,
|
|
390
389
|
});
|
|
391
|
-
// Reuse this directory's remembered ports so
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
390
|
+
// Reuse this directory's remembered ports so the Publisher keeps one address
|
|
391
|
+
// across sessions. While this directory's own session holds them
|
|
392
|
+
// (`--no-start` beside a running `cred dev`) they are busy because they are
|
|
393
|
+
// in use, so they are kept rather than probed: probing found them busy and
|
|
394
|
+
// moved the directory to a pair nothing was listening on.
|
|
396
395
|
const ports = options.port !== undefined
|
|
397
396
|
? yield chosenPair(directory, options.port, {
|
|
398
397
|
ownSessionRunning,
|
|
@@ -412,7 +411,6 @@ function wireDirectory(context, options) {
|
|
|
412
411
|
const configPath = sessionConfigPath(directory);
|
|
413
412
|
const mcp = (0, mcpconfig_1.writeMcpConfig)({
|
|
414
413
|
directory,
|
|
415
|
-
mcpPort: ports.mcpPort,
|
|
416
414
|
deployment: context.deployment,
|
|
417
415
|
organization,
|
|
418
416
|
});
|
|
@@ -438,7 +436,7 @@ function wireDirectory(context, options) {
|
|
|
438
436
|
// rewrites nothing and this line would be noise.
|
|
439
437
|
context.host.log("info", `Wrote .mcp.json. An agent already running in this directory will not see the new MCP servers until it restarts — for Claude Code, exit and start \`claude\` here again; it asks once to trust the project's servers.`);
|
|
440
438
|
}
|
|
441
|
-
(0, briefing_1.writeBriefing)(directory, {
|
|
439
|
+
const briefing = (0, briefing_1.writeBriefing)(directory, {
|
|
442
440
|
organization,
|
|
443
441
|
credibleEnvironment,
|
|
444
442
|
localEnvironment: config.environments[0].name,
|
|
@@ -482,7 +480,35 @@ function wireDirectory(context, options) {
|
|
|
482
480
|
// The pin is this developer's choice of environment, not a property of the
|
|
483
481
|
// models, so a teammate must not inherit it by cloning.
|
|
484
482
|
if ((0, folderconfig_1.ensureConfigIgnored)(directory) === "added") {
|
|
485
|
-
context.host.log("info", `Added ${folderconfig_1.FOLDER_CONFIG_DIR}/ to .
|
|
483
|
+
context.host.log("info", `Added ${folderconfig_1.FOLDER_CONFIG_DIR}/ to .git/info/exclude — it records the environment you chose, which is yours rather than the repository's.`);
|
|
484
|
+
}
|
|
485
|
+
// Agent files that hold nothing but what setup wrote are regenerated by
|
|
486
|
+
// every run, so they stay out of git like the pin. A file the repository
|
|
487
|
+
// tracks cannot be excluded, and one holding the user's own content is
|
|
488
|
+
// theirs to commit or not.
|
|
489
|
+
const agentFiles = [
|
|
490
|
+
...(mcp.onlyOurs ? [mcpconfig_1.MCP_CONFIG_FILENAME] : []),
|
|
491
|
+
...briefing
|
|
492
|
+
.filter((file) => file.onlyOurs)
|
|
493
|
+
.map((file) => path_1.default.basename(file.filePath)),
|
|
494
|
+
].filter((file) => !(0, git_1.isTrackedByGit)(directory, file));
|
|
495
|
+
(0, folderconfig_1.ensureIgnored)(directory, agentFiles, "agent files cred init writes; re-run it to regenerate them");
|
|
496
|
+
// A file an earlier run excluded that now holds the user's own content
|
|
497
|
+
// is theirs again, so git must show it.
|
|
498
|
+
(0, folderconfig_1.removeIgnored)(directory, [
|
|
499
|
+
...(mcp.onlyOurs ? [] : [mcpconfig_1.MCP_CONFIG_FILENAME]),
|
|
500
|
+
...briefing
|
|
501
|
+
.filter((file) => !file.onlyOurs)
|
|
502
|
+
.map((file) => path_1.default.basename(file.filePath)),
|
|
503
|
+
]);
|
|
504
|
+
const trackedAndChanged = [
|
|
505
|
+
...(mcp.changed ? [mcpconfig_1.MCP_CONFIG_FILENAME] : []),
|
|
506
|
+
...briefing
|
|
507
|
+
.filter((file) => file.action !== "unchanged")
|
|
508
|
+
.map((file) => path_1.default.basename(file.filePath)),
|
|
509
|
+
].filter((file) => (0, git_1.isTrackedByGit)(directory, file));
|
|
510
|
+
if (trackedAndChanged.length) {
|
|
511
|
+
context.host.log("info", `Updated what Credible manages in ${joinNames(trackedAndChanged)}, which git tracks, so it shows as a change to commit or discard.`);
|
|
486
512
|
}
|
|
487
513
|
if (skills) {
|
|
488
514
|
// Installer-managed files, regenerated by every setup and pinned in a
|
|
@@ -583,12 +609,12 @@ exports.PORT_FLAG = "--port";
|
|
|
583
609
|
* The pair `--port` names, refused when something else holds it.
|
|
584
610
|
*
|
|
585
611
|
* Unlike the remembered pair, which quietly gives way to another when busy, a
|
|
586
|
-
* chosen pair is used as given or not at all: moving off it would
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
*
|
|
612
|
+
* chosen pair is used as given or not at all: moving off it would serve
|
|
613
|
+
* somewhere the user did not ask for. This directory's own running session
|
|
614
|
+
* holding it is not a conflict (`--no-start` beside `cred dev`). That session
|
|
615
|
+
* running on a different pair is: moving the pin away from the live server
|
|
616
|
+
* means `cred mcp`, which finds the server through the pin, and the next
|
|
617
|
+
* ownership check would both miss it.
|
|
592
618
|
*/
|
|
593
619
|
function chosenPair(directory, uiPort, own) {
|
|
594
620
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -749,7 +775,7 @@ function runSetup(context, options) {
|
|
|
749
775
|
context.host.log("info",
|
|
750
776
|
// The last line an agent sees, so it repeats the restart: the agent
|
|
751
777
|
// that ran this has no `malloy` tools until it starts again.
|
|
752
|
-
`Wired ${wired.directory}. Next: restart any agent session in that directory so it loads .mcp.json,
|
|
778
|
+
`Wired ${wired.directory}. Next: restart any agent session in that directory so it loads .mcp.json; its \`malloy\` server starts the publisher through \`cred mcp\`. Without an agent, run \`cred dev\` there.`);
|
|
753
779
|
return { wired };
|
|
754
780
|
}
|
|
755
781
|
const running = yield startSession(context, wired, { open: options.open });
|