alchemy 2.0.0-beta.20 → 2.0.0-beta.21
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/bin/alchemy.js +1 -1
- package/bin/alchemy.js.map +1 -1
- package/bin/commands/bootstrap.ts +59 -57
- package/bin/commands/deploy.ts +8 -2
- package/bin/commands/login.ts +56 -49
- package/bin/commands/logs.ts +92 -90
- package/bin/commands/profile.ts +74 -70
- package/bin/commands/state.ts +160 -135
- package/bin/commands/tail.ts +94 -92
- package/lib/AWS/CloudFront/CachePolicy.d.ts.map +1 -1
- package/lib/AWS/CloudFront/CachePolicy.js.map +1 -1
- package/lib/AWS/CloudFront/KeyGroup.d.ts.map +1 -1
- package/lib/AWS/CloudFront/KeyGroup.js.map +1 -1
- package/lib/AWS/CloudFront/OriginRequestPolicy.d.ts.map +1 -1
- package/lib/AWS/CloudFront/OriginRequestPolicy.js +3 -1
- package/lib/AWS/CloudFront/OriginRequestPolicy.js.map +1 -1
- package/lib/AWS/CloudFront/PublicKey.d.ts.map +1 -1
- package/lib/AWS/CloudFront/PublicKey.js.map +1 -1
- package/lib/AWS/ECS/CapacityProvider.d.ts.map +1 -1
- package/lib/AWS/ECS/CapacityProvider.js +5 -4
- package/lib/AWS/ECS/CapacityProvider.js.map +1 -1
- package/lib/Axiom/Annotation.d.ts +3 -3
- package/lib/Axiom/Annotation.d.ts.map +1 -1
- package/lib/Axiom/Annotation.js +5 -5
- package/lib/Axiom/Annotation.js.map +1 -1
- package/lib/Axiom/ApiToken.d.ts +3 -3
- package/lib/Axiom/ApiToken.d.ts.map +1 -1
- package/lib/Axiom/ApiToken.js +4 -4
- package/lib/Axiom/ApiToken.js.map +1 -1
- package/lib/Axiom/AuthProvider.js.map +1 -1
- package/lib/Axiom/Credentials.d.ts +1 -1
- package/lib/Axiom/Credentials.d.ts.map +1 -1
- package/lib/Axiom/Credentials.js +2 -3
- package/lib/Axiom/Credentials.js.map +1 -1
- package/lib/Axiom/Dashboard.d.ts +4 -4
- package/lib/Axiom/Dashboard.d.ts.map +1 -1
- package/lib/Axiom/Dashboard.js +5 -5
- package/lib/Axiom/Dashboard.js.map +1 -1
- package/lib/Axiom/Dataset.d.ts.map +1 -1
- package/lib/Axiom/Dataset.js +5 -5
- package/lib/Axiom/Dataset.js.map +1 -1
- package/lib/Axiom/Monitor.d.ts +3 -3
- package/lib/Axiom/Monitor.d.ts.map +1 -1
- package/lib/Axiom/Monitor.js +6 -6
- package/lib/Axiom/Monitor.js.map +1 -1
- package/lib/Axiom/Notifier.d.ts +3 -3
- package/lib/Axiom/Notifier.d.ts.map +1 -1
- package/lib/Axiom/Notifier.js +9 -6
- package/lib/Axiom/Notifier.js.map +1 -1
- package/lib/Axiom/View.d.ts +3 -3
- package/lib/Axiom/View.d.ts.map +1 -1
- package/lib/Axiom/View.js +5 -5
- package/lib/Axiom/View.js.map +1 -1
- package/lib/Axiom/VirtualField.d.ts +3 -3
- package/lib/Axiom/VirtualField.d.ts.map +1 -1
- package/lib/Axiom/VirtualField.js +5 -5
- package/lib/Axiom/VirtualField.js.map +1 -1
- package/lib/Bundle/PurePlugin.d.ts.map +1 -1
- package/lib/Bundle/PurePlugin.js +4 -4
- package/lib/Bundle/PurePlugin.js.map +1 -1
- package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
- package/lib/Cloudflare/ApiToken/AccountApiToken.js.map +1 -1
- package/lib/Cloudflare/ApiToken/Common.d.ts.map +1 -1
- package/lib/Cloudflare/ApiToken/Common.js.map +1 -1
- package/lib/Cloudflare/ApiToken/UserApiToken.d.ts.map +1 -1
- package/lib/Cloudflare/ApiToken/UserApiToken.js.map +1 -1
- package/lib/Cloudflare/Fetcher.d.ts.map +1 -1
- package/lib/Cloudflare/Fetcher.js +3 -1
- package/lib/Cloudflare/Fetcher.js.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +12 -3
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Output.d.ts.map +1 -1
- package/lib/Output.js +1 -1
- package/lib/Output.js.map +1 -1
- package/lib/Telemetry/Layer.d.ts.map +1 -1
- package/lib/Telemetry/Layer.js.map +1 -1
- package/lib/Telemetry/Metrics.d.ts.map +1 -1
- package/lib/Telemetry/Metrics.js.map +1 -1
- package/package.json +5 -5
- package/src/AWS/CloudFront/CachePolicy.ts +5 -2
- package/src/AWS/CloudFront/KeyGroup.ts +2 -1
- package/src/AWS/CloudFront/OriginRequestPolicy.ts +8 -2
- package/src/AWS/CloudFront/PublicKey.ts +2 -1
- package/src/AWS/ECS/CapacityProvider.ts +12 -9
- package/src/Axiom/Annotation.ts +7 -7
- package/src/Axiom/ApiToken.ts +6 -6
- package/src/Axiom/AuthProvider.ts +2 -7
- package/src/Axiom/Credentials.ts +2 -6
- package/src/Axiom/Dashboard.ts +10 -13
- package/src/Axiom/Dataset.ts +8 -12
- package/src/Axiom/Monitor.ts +8 -8
- package/src/Axiom/Notifier.ts +11 -8
- package/src/Axiom/View.ts +7 -7
- package/src/Axiom/VirtualField.ts +7 -7
- package/src/Bundle/PurePlugin.ts +6 -5
- package/src/Cloudflare/ApiToken/AccountApiToken.ts +1 -3
- package/src/Cloudflare/ApiToken/Common.ts +1 -3
- package/src/Cloudflare/ApiToken/UserApiToken.ts +1 -3
- package/src/Cloudflare/Fetcher.ts +3 -1
- package/src/Cloudflare/Workers/Worker.ts +40 -27
- package/src/Output.ts +1 -2
- package/src/Telemetry/Layer.ts +9 -10
- package/src/Telemetry/Metrics.ts +1 -6
|
@@ -57,68 +57,70 @@ export const bootstrapCommand = Command.make(
|
|
|
57
57
|
"alchemy.region": a.region ?? "",
|
|
58
58
|
"alchemy.destroy": a.destroy,
|
|
59
59
|
}),
|
|
60
|
-
)(
|
|
61
|
-
|
|
60
|
+
)(
|
|
61
|
+
Effect.fnUntraced(function* ({ envFile, profile, region, destroy }) {
|
|
62
|
+
const logger = Logger.layer([fileLogger("bootstrap.txt")]);
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// Build a single AWSEnvironment, then derive Region/Credentials from
|
|
72
|
-
// it so resource providers downstream see a consistent view. The
|
|
73
|
-
// credentials Effect captures FileSystem/Path/HttpClient via the
|
|
74
|
-
// ambient context; AWSEnvironment expects R=never, so we provide it
|
|
75
|
-
// here.
|
|
76
|
-
const ambient = yield* Effect.context<FileSystem | Path | HttpClient>();
|
|
77
|
-
const environment = AWSEnvironment.makeEnvironment({
|
|
78
|
-
accountId: ssoProfile.sso_account_id,
|
|
79
|
-
region: region ?? ssoProfile.region ?? "us-east-1",
|
|
80
|
-
credentials: Auth.loadProfileCredentials(profile).pipe(
|
|
81
|
-
Effect.provide(ambient),
|
|
82
|
-
),
|
|
83
|
-
profile,
|
|
84
|
-
});
|
|
64
|
+
return yield* Effect.gen(function* () {
|
|
65
|
+
const ssoProfile = yield* Auth.loadProfile(profile);
|
|
66
|
+
if (!ssoProfile.sso_account_id) {
|
|
67
|
+
return yield* Effect.die(
|
|
68
|
+
`AWS SSO profile '${profile}' is missing sso_account_id`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
85
71
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
72
|
+
// Build a single AWSEnvironment, then derive Region/Credentials from
|
|
73
|
+
// it so resource providers downstream see a consistent view. The
|
|
74
|
+
// credentials Effect captures FileSystem/Path/HttpClient via the
|
|
75
|
+
// ambient context; AWSEnvironment expects R=never, so we provide it
|
|
76
|
+
// here.
|
|
77
|
+
const ambient = yield* Effect.context<FileSystem | Path | HttpClient>();
|
|
78
|
+
const environment = AWSEnvironment.makeEnvironment({
|
|
79
|
+
accountId: ssoProfile.sso_account_id,
|
|
80
|
+
region: region ?? ssoProfile.region ?? "us-east-1",
|
|
81
|
+
credentials: Auth.loadProfileCredentials(profile).pipe(
|
|
82
|
+
Effect.provide(ambient),
|
|
83
|
+
),
|
|
84
|
+
profile,
|
|
85
|
+
});
|
|
93
86
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
87
|
+
const awsLayers = Layer.provideMerge(
|
|
88
|
+
Layer.mergeAll(
|
|
89
|
+
AWSRegion.fromEnvironment,
|
|
90
|
+
AWSCredentials.fromEnvironment,
|
|
91
|
+
),
|
|
92
|
+
environment,
|
|
99
93
|
);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
94
|
+
|
|
95
|
+
return yield* Effect.gen(function* () {
|
|
96
|
+
const provider = yield* loadConfigProvider(envFile);
|
|
97
|
+
const bootstrapLayer = Layer.provide(
|
|
98
|
+
awsLayers,
|
|
99
|
+
Layer.succeed(ConfigProvider.ConfigProvider, provider),
|
|
100
|
+
);
|
|
101
|
+
if (destroy) {
|
|
102
|
+
yield* destroyBootstrapAws().pipe(
|
|
103
|
+
Effect.tap((result) =>
|
|
104
|
+
result.destroyed === 0
|
|
105
|
+
? Console.log("✓ No bootstrap buckets found to destroy")
|
|
106
|
+
: Console.log(
|
|
107
|
+
`✓ Destroyed ${result.destroyed} bootstrap bucket(s): ${result.bucketNames.join(", ")}`,
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
Effect.provide(bootstrapLayer),
|
|
111
|
+
);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
yield* bootstrapAws().pipe(
|
|
115
|
+
Effect.tap(({ bucketName, created }) =>
|
|
116
|
+
created
|
|
117
|
+
? Console.log(`✓ Created assets bucket: ${bucketName}`)
|
|
118
|
+
: Console.log(`✓ Assets bucket already exists: ${bucketName}`),
|
|
108
119
|
),
|
|
109
120
|
Effect.provide(bootstrapLayer),
|
|
110
121
|
);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
created
|
|
116
|
-
? Console.log(`✓ Created assets bucket: ${bucketName}`)
|
|
117
|
-
: Console.log(`✓ Assets bucket already exists: ${bucketName}`),
|
|
118
|
-
),
|
|
119
|
-
Effect.provide(bootstrapLayer),
|
|
120
|
-
);
|
|
121
|
-
});
|
|
122
|
-
}).pipe(Effect.provide(logger));
|
|
123
|
-
})),
|
|
122
|
+
});
|
|
123
|
+
}).pipe(Effect.provide(logger));
|
|
124
|
+
}),
|
|
125
|
+
),
|
|
124
126
|
);
|
package/bin/commands/deploy.ts
CHANGED
|
@@ -149,7 +149,10 @@ export const destroyCommand = Command.make(
|
|
|
149
149
|
yes,
|
|
150
150
|
profile,
|
|
151
151
|
},
|
|
152
|
-
instrumentCommand(
|
|
152
|
+
instrumentCommand(
|
|
153
|
+
"destroy",
|
|
154
|
+
stackSpanAttrs,
|
|
155
|
+
)((args) =>
|
|
153
156
|
execStack({
|
|
154
157
|
...args,
|
|
155
158
|
destroy: true,
|
|
@@ -165,7 +168,10 @@ export const planCommand = Command.make(
|
|
|
165
168
|
stage,
|
|
166
169
|
profile,
|
|
167
170
|
},
|
|
168
|
-
instrumentCommand(
|
|
171
|
+
instrumentCommand(
|
|
172
|
+
"plan",
|
|
173
|
+
stackSpanAttrs,
|
|
174
|
+
)((args) =>
|
|
169
175
|
execStack({
|
|
170
176
|
...args,
|
|
171
177
|
// plan is the same as deploy with dryRun always set to true
|
package/bin/commands/login.ts
CHANGED
|
@@ -45,67 +45,74 @@ export const loginCommand = Command.make(
|
|
|
45
45
|
},
|
|
46
46
|
instrumentCommand(
|
|
47
47
|
"login",
|
|
48
|
-
(a: {
|
|
48
|
+
(a: {
|
|
49
|
+
main: string;
|
|
50
|
+
stage: string;
|
|
51
|
+
profile: string;
|
|
52
|
+
configure: boolean;
|
|
53
|
+
}) => ({
|
|
49
54
|
"alchemy.stage": a.stage,
|
|
50
55
|
"alchemy.profile": a.profile,
|
|
51
56
|
"alchemy.main": a.main,
|
|
52
57
|
"alchemy.configure": a.configure,
|
|
53
58
|
}),
|
|
54
|
-
)(
|
|
55
|
-
|
|
59
|
+
)(
|
|
60
|
+
Effect.fnUntraced(function* ({ main, stage, envFile, profile, configure }) {
|
|
61
|
+
const stackEffect = yield* importStack(main);
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
const authProviders: AuthProviders["Service"] = {};
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
),
|
|
64
|
-
Logger.layer([fileLogger("out")]),
|
|
65
|
-
Layer.succeed(Stage, stage),
|
|
66
|
-
State.localState(),
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
yield* Effect.gen(function* () {
|
|
70
|
-
yield* Effect.catchCause(stackEffect, (cause) =>
|
|
71
|
-
Console.warn(
|
|
72
|
-
`Ignoring error while building stack for login (likely due to missing or broken credentials):\n${Cause.pretty(cause)}`,
|
|
65
|
+
const services = Layer.mergeAll(
|
|
66
|
+
Layer.succeed(AuthProviders, authProviders),
|
|
67
|
+
ConfigProvider.layer(
|
|
68
|
+
withProfileOverride(yield* loadConfigProvider(envFile), profile),
|
|
73
69
|
),
|
|
70
|
+
Logger.layer([fileLogger("out")]),
|
|
71
|
+
Layer.succeed(Stage, stage),
|
|
72
|
+
State.localState(),
|
|
74
73
|
);
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"No AuthProviders registered. Make sure the stack's providers() layer includes AuthProviderLayer entries.",
|
|
75
|
+
yield* Effect.gen(function* () {
|
|
76
|
+
yield* Effect.catchCause(stackEffect, (cause) =>
|
|
77
|
+
Console.warn(
|
|
78
|
+
`Ignoring error while building stack for login (likely due to missing or broken credentials):\n${Cause.pretty(cause)}`,
|
|
79
|
+
),
|
|
82
80
|
);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
81
|
|
|
86
|
-
|
|
87
|
-
providers
|
|
88
|
-
(provider) =>
|
|
89
|
-
Effect.gen(function* () {
|
|
90
|
-
const existing = yield* getProfile(profile);
|
|
91
|
-
const stored = existing?.[provider.name];
|
|
82
|
+
const ci = yield* Config.boolean("CI").pipe(Config.withDefault(false));
|
|
83
|
+
const providers = Object.values(authProviders);
|
|
92
84
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
} else {
|
|
101
|
-
cfg = stored;
|
|
102
|
-
}
|
|
85
|
+
if (providers.length === 0) {
|
|
86
|
+
yield* Console.log(
|
|
87
|
+
"No AuthProviders registered. Make sure the stack's providers() layer includes AuthProviderLayer entries.",
|
|
88
|
+
);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
103
91
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
92
|
+
yield* Effect.forEach(
|
|
93
|
+
providers,
|
|
94
|
+
(provider) =>
|
|
95
|
+
Effect.gen(function* () {
|
|
96
|
+
const existing = yield* getProfile(profile);
|
|
97
|
+
const stored = existing?.[provider.name];
|
|
98
|
+
|
|
99
|
+
let cfg: { method: string };
|
|
100
|
+
if (configure || stored == null) {
|
|
101
|
+
cfg = yield* provider.configure(profile, { ci });
|
|
102
|
+
yield* setProfile(profile, {
|
|
103
|
+
...existing,
|
|
104
|
+
[provider.name]: cfg,
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
cfg = stored;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
yield* provider.login(profile, cfg);
|
|
111
|
+
yield* provider.prettyPrint(profile, cfg);
|
|
112
|
+
}),
|
|
113
|
+
{ discard: true },
|
|
114
|
+
);
|
|
115
|
+
}).pipe(Effect.provide(services));
|
|
116
|
+
}),
|
|
117
|
+
),
|
|
111
118
|
);
|
package/bin/commands/logs.ts
CHANGED
|
@@ -61,106 +61,108 @@ export const logsCommand = Command.make(
|
|
|
61
61
|
"alchemy.main": a.main,
|
|
62
62
|
"alchemy.limit": a.limit,
|
|
63
63
|
}),
|
|
64
|
-
)(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
yield* Effect.gen(function* () {
|
|
88
|
-
const state = yield* State.State;
|
|
89
|
-
const stack = yield* stackEffect;
|
|
64
|
+
)(
|
|
65
|
+
Effect.fnUntraced(function* ({
|
|
66
|
+
main,
|
|
67
|
+
stage,
|
|
68
|
+
envFile,
|
|
69
|
+
profile,
|
|
70
|
+
filter,
|
|
71
|
+
limit,
|
|
72
|
+
since,
|
|
73
|
+
}) {
|
|
74
|
+
const stackEffect = yield* importStack(main);
|
|
75
|
+
|
|
76
|
+
const services = Layer.mergeAll(
|
|
77
|
+
ConfigProvider.layer(
|
|
78
|
+
withProfileOverride(yield* loadConfigProvider(envFile), profile),
|
|
79
|
+
),
|
|
80
|
+
Layer.succeed(AuthProviders, {}),
|
|
81
|
+
Layer.succeed(Stage, stage),
|
|
82
|
+
Logger.layer([fileLogger("out")]),
|
|
83
|
+
State.localState(),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const sinceDate = since ? parseSince(since) : undefined;
|
|
90
87
|
|
|
91
88
|
yield* Effect.gen(function* () {
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
89
|
+
const state = yield* State.State;
|
|
90
|
+
const stack = yield* stackEffect;
|
|
91
|
+
|
|
92
|
+
yield* Effect.gen(function* () {
|
|
93
|
+
const filterSet = parseResourceFilter(filter);
|
|
94
|
+
const availableIds = [
|
|
95
|
+
...new Set(Object.values(stack.resources).map((r) => r.LogicalId)),
|
|
96
|
+
].sort();
|
|
97
|
+
|
|
98
|
+
if (filterSet) {
|
|
99
|
+
for (const id of filterSet) {
|
|
100
|
+
if (!availableIds.includes(id)) {
|
|
101
|
+
return yield* Effect.die(
|
|
102
|
+
new Error(
|
|
103
|
+
`Unknown resource '${id}' in --filter. Available: ${availableIds.join(", ") || "(none)"}`,
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
|
-
}
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
const fqns = Object.keys(stack.resources);
|
|
111
|
+
const allLogs: { logicalId: string; lines: LogLine[] }[] = [];
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
for (const fqn of fqns) {
|
|
114
|
+
const resource = stack.resources[fqn]!;
|
|
115
|
+
if (filterSet && !filterSet.has(resource.LogicalId)) continue;
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
const resourceState = yield* state.get({
|
|
118
|
+
stack: stack.name,
|
|
119
|
+
stage: stack.stage,
|
|
120
|
+
fqn,
|
|
121
|
+
});
|
|
122
|
+
if (!resourceState?.attr) continue;
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
const provider = yield* findProviderByType(resource.Type);
|
|
125
|
+
if (!provider.logs) continue;
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
127
|
+
const lines = yield* provider.logs({
|
|
128
|
+
id: resource.LogicalId,
|
|
129
|
+
instanceId: resourceState.instanceId,
|
|
130
|
+
props: resourceState.props as any,
|
|
131
|
+
output: resourceState.attr as any,
|
|
132
|
+
options: { limit, since: sinceDate },
|
|
133
|
+
});
|
|
133
134
|
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
allLogs.push({ logicalId: resource.LogicalId, lines });
|
|
136
|
+
}
|
|
136
137
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
138
|
+
if (allLogs.length === 0) {
|
|
139
|
+
if (filterSet) {
|
|
140
|
+
yield* Console.log(
|
|
141
|
+
"No resources with logs match --filter (deploy first, or selected resources may not expose logs).",
|
|
142
|
+
);
|
|
143
|
+
} else {
|
|
144
|
+
yield* Console.log(
|
|
145
|
+
"No resources with logs found. Deploy first, then run logs.",
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
146
149
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
})),
|
|
150
|
+
|
|
151
|
+
const merged = allLogs
|
|
152
|
+
.flatMap(({ logicalId, lines }, i) => {
|
|
153
|
+
const color = TAIL_COLORS[i % TAIL_COLORS.length]!;
|
|
154
|
+
return lines.map((line) => ({
|
|
155
|
+
...line,
|
|
156
|
+
formatted: `${color}${formatLocalTimestamp(line.timestamp)} [${logicalId}]${TAIL_RESET} ${line.message}`,
|
|
157
|
+
}));
|
|
158
|
+
})
|
|
159
|
+
.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
160
|
+
|
|
161
|
+
for (const entry of merged) {
|
|
162
|
+
yield* Console.log(entry.formatted);
|
|
163
|
+
}
|
|
164
|
+
}).pipe(Effect.provide(stack.services));
|
|
165
|
+
}).pipe(Effect.provide(services));
|
|
166
|
+
}),
|
|
167
|
+
),
|
|
166
168
|
);
|
package/bin/commands/profile.ts
CHANGED
|
@@ -23,88 +23,92 @@ import { envFile, instrumentCommand, profile } from "./_shared.ts";
|
|
|
23
23
|
const showCommand = Command.make(
|
|
24
24
|
"show",
|
|
25
25
|
{ profile, envFile },
|
|
26
|
-
instrumentCommand(
|
|
27
|
-
"profile.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
instrumentCommand("profile.show", (a: { profile: string }) => ({
|
|
27
|
+
"alchemy.profile": a.profile,
|
|
28
|
+
}))(
|
|
29
|
+
Effect.fnUntraced(function* ({ profile, envFile }) {
|
|
30
|
+
const stored = yield* getProfile(profile);
|
|
31
|
+
if (stored == null) {
|
|
32
|
+
const config = yield* readConfig;
|
|
33
|
+
const names = Object.keys(config.profiles);
|
|
34
|
+
yield* Console.log(`Profile '${profile}' not found.`);
|
|
35
|
+
if (names.length > 0) {
|
|
36
|
+
yield* Console.log(`Available profiles: ${names.sort().join(", ")}`);
|
|
37
|
+
} else {
|
|
38
|
+
yield* Console.log("No profiles configured. Run `alchemy login`.");
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
39
41
|
}
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
yield* Console.log(`Profile: ${profile}`);
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
const providerNames = Object.keys(stored).sort();
|
|
46
|
+
if (providerNames.length === 0) {
|
|
47
|
+
yield* Console.log("(no providers configured)");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
const authProviders: AuthProviders["Service"] = {};
|
|
52
|
+
const authRegistry = Layer.succeed(AuthProviders, authProviders);
|
|
53
|
+
const services = Layer.mergeAll(
|
|
54
|
+
authRegistry,
|
|
55
|
+
ConfigProvider.layer(
|
|
56
|
+
withProfileOverride(yield* loadConfigProvider(envFile), profile),
|
|
57
|
+
),
|
|
58
|
+
Logger.layer([fileLogger("out")]),
|
|
59
|
+
// Building these layers triggers their AuthProviderLayer effect, which
|
|
60
|
+
// registers the provider into the shared `authProviders` registry.
|
|
61
|
+
Layer.provide(Layer.mergeAll(CloudflareAuth, AwsAuth), authRegistry),
|
|
62
|
+
);
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
yield* Effect.gen(function* () {
|
|
65
|
+
for (const name of providerNames) {
|
|
66
|
+
const cfg = stored[name]!;
|
|
67
|
+
yield* Console.log("");
|
|
68
|
+
yield* Console.log(`── ${name} ──`);
|
|
69
|
+
const provider = authProviders[name];
|
|
70
|
+
if (provider == null) {
|
|
71
|
+
// Unknown provider — fall back to a JSON dump so the user can still
|
|
72
|
+
// see what's stored even if its module isn't bundled into the CLI.
|
|
73
|
+
yield* Console.log(`method: ${cfg.method}`);
|
|
74
|
+
const { method: _method, ...rest } = cfg as Record<
|
|
75
|
+
string,
|
|
76
|
+
unknown
|
|
77
|
+
> & { method: string };
|
|
78
|
+
for (const [k, v] of Object.entries(rest)) {
|
|
79
|
+
yield* Console.log(
|
|
80
|
+
`${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
continue;
|
|
80
84
|
}
|
|
81
|
-
|
|
85
|
+
yield* provider.prettyPrint(profile, cfg);
|
|
82
86
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
})),
|
|
87
|
+
}).pipe(Effect.provide(services));
|
|
88
|
+
}),
|
|
89
|
+
),
|
|
87
90
|
);
|
|
88
91
|
|
|
89
92
|
const clearCommand = Command.make(
|
|
90
93
|
"clear",
|
|
91
94
|
{ profile },
|
|
92
|
-
instrumentCommand(
|
|
93
|
-
"profile.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
95
|
+
instrumentCommand("profile.clear", (a: { profile: string }) => ({
|
|
96
|
+
"alchemy.profile": a.profile,
|
|
97
|
+
}))(
|
|
98
|
+
Effect.fnUntraced(function* ({ profile }) {
|
|
99
|
+
const removed = yield* deleteProfile(profile);
|
|
100
|
+
yield* deleteProfileCredentials(profile);
|
|
101
|
+
if (removed) {
|
|
102
|
+
yield* Console.log(
|
|
103
|
+
`Cleared profile '${profile}' and all its credentials.`,
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
yield* Console.log(
|
|
107
|
+
`Profile '${profile}' not found in profiles.json; removed any stray credentials directory.`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}),
|
|
111
|
+
),
|
|
108
112
|
);
|
|
109
113
|
|
|
110
114
|
export const profileCommand = Command.make("profile", {}).pipe(
|