@gitpod/gitpod-protocol 0.1.5-pd-ide-metrics.57 → 0.1.5-pd-ide-service.12
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/data/gitpod-schema.json +63 -129
- package/lib/admin-protocol.d.ts +3 -0
- package/lib/admin-protocol.d.ts.map +1 -1
- package/lib/admin-protocol.js.map +1 -1
- package/lib/billing-mode.d.ts +9 -4
- package/lib/billing-mode.d.ts.map +1 -1
- package/lib/billing-mode.js +15 -16
- package/lib/billing-mode.js.map +1 -1
- package/lib/gitpod-service.d.ts +17 -17
- package/lib/gitpod-service.d.ts.map +1 -1
- package/lib/gitpod-service.js +12 -0
- package/lib/gitpod-service.js.map +1 -1
- package/lib/ide-protocol.d.ts +8 -0
- package/lib/ide-protocol.d.ts.map +1 -1
- package/lib/messaging/error.d.ts +4 -1
- package/lib/messaging/error.d.ts.map +1 -1
- package/lib/messaging/error.js +9 -3
- package/lib/messaging/error.js.map +1 -1
- package/lib/plans.d.ts +1 -0
- package/lib/plans.d.ts.map +1 -1
- package/lib/plans.js +4 -0
- package/lib/plans.js.map +1 -1
- package/lib/protocol.d.ts +22 -3
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +27 -1
- package/lib/protocol.js.map +1 -1
- package/lib/teams-projects-protocol.d.ts +0 -1
- package/lib/teams-projects-protocol.d.ts.map +1 -1
- package/lib/teams-projects-protocol.js.map +1 -1
- package/lib/usage.d.ts +48 -17
- package/lib/usage.d.ts.map +1 -1
- package/lib/usage.js +6 -6
- package/lib/usage.js.map +1 -1
- package/lib/util/garbage-collected-cache.d.ts.map +1 -1
- package/lib/util/garbage-collected-cache.js +5 -0
- package/lib/util/garbage-collected-cache.js.map +1 -1
- package/lib/util/logging.spec.d.ts +7 -0
- package/lib/util/logging.spec.d.ts.map +1 -0
- package/lib/util/logging.spec.js +52 -0
- package/lib/util/logging.spec.js.map +1 -0
- package/lib/util/nice-grpc.d.ts +9 -0
- package/lib/util/nice-grpc.d.ts.map +1 -0
- package/lib/util/nice-grpc.js +119 -0
- package/lib/util/nice-grpc.js.map +1 -0
- package/lib/util/timeutil.d.ts +1 -0
- package/lib/util/timeutil.d.ts.map +1 -1
- package/lib/util/timeutil.js +7 -1
- package/lib/util/timeutil.js.map +1 -1
- package/lib/util/timeutil.spec.d.ts +2 -0
- package/lib/util/timeutil.spec.d.ts.map +1 -1
- package/lib/util/timeutil.spec.js +34 -0
- package/lib/util/timeutil.spec.js.map +1 -1
- package/lib/workspace-class.d.ts +1 -1
- package/lib/workspace-class.d.ts.map +1 -1
- package/lib/workspace-instance.d.ts +1 -0
- package/lib/workspace-instance.d.ts.map +1 -1
- package/package.json +4 -2
- package/pkg-yarn.lock +4 -2
- package/provenance-bundle.jsonl +1 -1
- package/src/admin-protocol.ts +3 -0
- package/src/billing-mode.ts +26 -15
- package/src/gitpod-service.ts +30 -17
- package/src/ide-protocol.ts +10 -0
- package/src/messaging/error.ts +13 -4
- package/src/plans.ts +4 -0
- package/src/protocol.ts +44 -4
- package/src/teams-projects-protocol.ts +0 -1
- package/src/usage.ts +52 -30
- package/src/util/garbage-collected-cache.ts +5 -0
- package/src/util/logging.spec.ts +23 -0
- package/src/util/nice-grpc.ts +93 -0
- package/src/util/timeutil.spec.ts +28 -1
- package/src/util/timeutil.ts +6 -0
- package/src/workspace-class.ts +1 -1
- package/src/workspace-instance.ts +3 -0
package/src/plans.ts
CHANGED
|
@@ -482,6 +482,10 @@ export namespace Plans {
|
|
|
482
482
|
);
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
+
export function isFreeTier(chargebeeId: string | undefined): boolean {
|
|
486
|
+
return chargebeeId === Plans.FREE.chargebeeId || chargebeeId === Plans.FREE_50.chargebeeId;
|
|
487
|
+
}
|
|
488
|
+
|
|
485
489
|
export function isFreeNonTransientPlan(chargebeeId: string | undefined): boolean {
|
|
486
490
|
return chargebeeId === Plans.FREE_OPEN_SOURCE.chargebeeId;
|
|
487
491
|
}
|
package/src/protocol.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { WorkspaceInstance, PortVisibility } from "./workspace-instance";
|
|
|
8
8
|
import { RoleOrPermission } from "./permission";
|
|
9
9
|
import { Project } from "./teams-projects-protocol";
|
|
10
10
|
import { createHash } from "crypto";
|
|
11
|
+
import { AttributionId } from "./attribution";
|
|
11
12
|
|
|
12
13
|
export interface UserInfo {
|
|
13
14
|
name?: string;
|
|
@@ -48,6 +49,12 @@ export interface User {
|
|
|
48
49
|
|
|
49
50
|
// Identifies an explicit team or user ID to which all the user's workspace usage should be attributed to (e.g. for billing purposes)
|
|
50
51
|
usageAttributionId?: string;
|
|
52
|
+
|
|
53
|
+
// The last time this user got verified somehow. The user is not verified if this is empty.
|
|
54
|
+
lastVerificationTime?: string;
|
|
55
|
+
|
|
56
|
+
// The phone number used for the last phone verification.
|
|
57
|
+
verificationPhoneNumber?: string;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
export namespace User {
|
|
@@ -207,7 +214,21 @@ export interface AdditionalUserData {
|
|
|
207
214
|
// additional user profile data
|
|
208
215
|
profile?: ProfileDetails;
|
|
209
216
|
}
|
|
210
|
-
|
|
217
|
+
export namespace AdditionalUserData {
|
|
218
|
+
export function set(user: User, partialData: Partial<AdditionalUserData>): User {
|
|
219
|
+
if (!user.additionalData) {
|
|
220
|
+
user.additionalData = {
|
|
221
|
+
...partialData,
|
|
222
|
+
};
|
|
223
|
+
} else {
|
|
224
|
+
user.additionalData = {
|
|
225
|
+
...user.additionalData,
|
|
226
|
+
...partialData,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
return user;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
211
232
|
// The format in which we store User Profiles in
|
|
212
233
|
export interface ProfileDetails {
|
|
213
234
|
// when was the last time the user updated their profile information or has been nudged to do so.
|
|
@@ -273,8 +294,19 @@ export const WorkspaceFeatureFlags = {
|
|
|
273
294
|
full_workspace_backup: undefined,
|
|
274
295
|
persistent_volume_claim: undefined,
|
|
275
296
|
protected_secrets: undefined,
|
|
297
|
+
workspace_class_limiting: undefined,
|
|
298
|
+
workspace_connection_limiting: undefined,
|
|
276
299
|
};
|
|
277
300
|
export type NamedWorkspaceFeatureFlag = keyof typeof WorkspaceFeatureFlags;
|
|
301
|
+
export namespace NamedWorkspaceFeatureFlag {
|
|
302
|
+
export const WORKSPACE_PERSISTED_FEATTURE_FLAGS: NamedWorkspaceFeatureFlag[] = [
|
|
303
|
+
"full_workspace_backup",
|
|
304
|
+
"persistent_volume_claim",
|
|
305
|
+
];
|
|
306
|
+
export function isWorkspacePersisted(ff: NamedWorkspaceFeatureFlag): boolean {
|
|
307
|
+
return WORKSPACE_PERSISTED_FEATTURE_FLAGS.includes(ff);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
278
310
|
|
|
279
311
|
export interface EnvVarWithValue {
|
|
280
312
|
name: string;
|
|
@@ -781,6 +813,12 @@ export interface RepositoryCloneInformation {
|
|
|
781
813
|
checkoutLocation?: string;
|
|
782
814
|
}
|
|
783
815
|
|
|
816
|
+
export interface CoreDumpConfig {
|
|
817
|
+
enabled?: boolean;
|
|
818
|
+
softLimit?: number;
|
|
819
|
+
hardLimit?: number;
|
|
820
|
+
}
|
|
821
|
+
|
|
784
822
|
export interface WorkspaceConfig {
|
|
785
823
|
mainConfiguration?: string;
|
|
786
824
|
additionalRepositories?: RepositoryCloneInformation[];
|
|
@@ -793,6 +831,7 @@ export interface WorkspaceConfig {
|
|
|
793
831
|
github?: GithubAppConfig;
|
|
794
832
|
vscode?: VSCodeConfig;
|
|
795
833
|
jetbrains?: JetBrainsConfig;
|
|
834
|
+
coreDump?: CoreDumpConfig;
|
|
796
835
|
|
|
797
836
|
/** deprecated. Enabled by default **/
|
|
798
837
|
experimentalNetwork?: boolean;
|
|
@@ -801,13 +840,12 @@ export interface WorkspaceConfig {
|
|
|
801
840
|
* Where the config object originates from.
|
|
802
841
|
*
|
|
803
842
|
* repo - from the repository
|
|
804
|
-
* project-db - from the "Project" stored in the database
|
|
805
843
|
* definitly-gp - from github.com/gitpod-io/definitely-gp
|
|
806
844
|
* derived - computed based on analyzing the repository
|
|
807
845
|
* additional-content - config comes from additional content, usually provided through the project's configuration
|
|
808
846
|
* default - our static catch-all default config
|
|
809
847
|
*/
|
|
810
|
-
_origin?: "repo" | "
|
|
848
|
+
_origin?: "repo" | "definitely-gp" | "derived" | "additional-content" | "default";
|
|
811
849
|
|
|
812
850
|
/**
|
|
813
851
|
* Set of automatically infered feature flags. That's not something the user can set, but
|
|
@@ -1482,10 +1520,12 @@ export interface Terms {
|
|
|
1482
1520
|
readonly formElements?: object;
|
|
1483
1521
|
}
|
|
1484
1522
|
|
|
1523
|
+
export type BillingStrategy = "other" | "stripe";
|
|
1485
1524
|
export interface CostCenter {
|
|
1486
|
-
readonly id:
|
|
1525
|
+
readonly id: AttributionId;
|
|
1487
1526
|
/**
|
|
1488
1527
|
* Unit: credits
|
|
1489
1528
|
*/
|
|
1490
1529
|
spendingLimit: number;
|
|
1530
|
+
billingStrategy: BillingStrategy;
|
|
1491
1531
|
}
|
package/src/usage.ts
CHANGED
|
@@ -6,45 +6,67 @@
|
|
|
6
6
|
|
|
7
7
|
import { WorkspaceType } from "./protocol";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// types below are copied over from components/usage-api/typescript/src/usage/v1/usage_pb.d.ts
|
|
10
|
+
export interface ListUsageRequest {
|
|
11
11
|
attributionId: string;
|
|
12
|
+
from?: number;
|
|
13
|
+
to?: number;
|
|
14
|
+
order: Ordering;
|
|
15
|
+
pagination?: PaginationRequest;
|
|
16
|
+
}
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
instanceId: string;
|
|
18
|
-
|
|
19
|
-
workspaceId: string;
|
|
20
|
-
|
|
21
|
-
workspaceType: BillableWorkspaceType;
|
|
22
|
-
|
|
23
|
-
workspaceClass: string;
|
|
24
|
-
|
|
25
|
-
// When the workspace started
|
|
26
|
-
startTime: string;
|
|
18
|
+
export enum Ordering {
|
|
19
|
+
ORDERING_DESCENDING = 0,
|
|
20
|
+
ORDERING_ASCENDING = 1,
|
|
21
|
+
}
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
export interface PaginationRequest {
|
|
24
|
+
perPage: number;
|
|
25
|
+
page: number;
|
|
26
|
+
}
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
export interface ListUsageResponse {
|
|
29
|
+
usageEntriesList: Usage[];
|
|
30
|
+
pagination?: PaginationResponse;
|
|
31
|
+
creditBalanceAtStart: number;
|
|
32
|
+
creditBalanceAtEnd: number;
|
|
33
|
+
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
export interface PaginationResponse {
|
|
36
|
+
perPage: number;
|
|
37
|
+
totalPages: number;
|
|
38
|
+
total: number;
|
|
39
|
+
page: number;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
export
|
|
42
|
+
export type UsageKind = "workspaceinstance" | "invoice";
|
|
43
|
+
export interface Usage {
|
|
44
|
+
id: string;
|
|
39
45
|
attributionId: string;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
description: string;
|
|
47
|
+
credits: number;
|
|
48
|
+
effectiveTime?: number;
|
|
49
|
+
kind: UsageKind;
|
|
50
|
+
workspaceInstanceId: string;
|
|
51
|
+
draft: boolean;
|
|
52
|
+
metadata: WorkspaceInstanceUsageData | InvoiceUsageData;
|
|
43
53
|
}
|
|
44
54
|
|
|
45
|
-
|
|
55
|
+
// the equivalent golang shape is maintained in `/workspace/gitpod/`components/usage/pkg/db/usage.go`
|
|
56
|
+
export interface WorkspaceInstanceUsageData {
|
|
57
|
+
workspaceId: string;
|
|
58
|
+
workspaceType: WorkspaceType;
|
|
59
|
+
workspaceClass: string;
|
|
60
|
+
contextURL: string;
|
|
61
|
+
startTime: string;
|
|
62
|
+
endTime?: string;
|
|
63
|
+
userId: string;
|
|
64
|
+
userName: string;
|
|
65
|
+
userAvatarURL: string;
|
|
66
|
+
}
|
|
46
67
|
|
|
47
|
-
export
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
export interface InvoiceUsageData {
|
|
69
|
+
invoiceId: string;
|
|
70
|
+
startDate: string;
|
|
71
|
+
endDate: string;
|
|
50
72
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { suite, test } from "mocha-typescript";
|
|
8
|
+
import { log } from "./logging";
|
|
9
|
+
|
|
10
|
+
@suite
|
|
11
|
+
class TestLogging {
|
|
12
|
+
@test public async testLogInfo_output() {
|
|
13
|
+
const testObj = {
|
|
14
|
+
null: null,
|
|
15
|
+
undefined: undefined,
|
|
16
|
+
empty: "",
|
|
17
|
+
foo: "bar",
|
|
18
|
+
number: 0,
|
|
19
|
+
};
|
|
20
|
+
log.info("info logging test", testObj);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
module.exports = new TestLogging();
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { isAbortError } from "abort-controller-x";
|
|
8
|
+
import {
|
|
9
|
+
CallOptions,
|
|
10
|
+
ClientError,
|
|
11
|
+
ClientMiddleware,
|
|
12
|
+
ClientMiddlewareCall,
|
|
13
|
+
Status,
|
|
14
|
+
MethodDescriptor,
|
|
15
|
+
} from "nice-grpc-common";
|
|
16
|
+
import { GrpcMethodType, IClientCallMetrics } from "./grpc";
|
|
17
|
+
|
|
18
|
+
function getLabels(method: MethodDescriptor) {
|
|
19
|
+
const callType = method.requestStream
|
|
20
|
+
? method.responseStream
|
|
21
|
+
? "bidi_stream"
|
|
22
|
+
: "client_stream"
|
|
23
|
+
: method.responseStream
|
|
24
|
+
? "server_stream"
|
|
25
|
+
: "unary";
|
|
26
|
+
const { path } = method;
|
|
27
|
+
const [serviceName, methodName] = path.split("/").slice(1);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
type: callType as GrpcMethodType,
|
|
31
|
+
service: serviceName,
|
|
32
|
+
method: methodName,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function* incrementStreamMessagesCounter<T>(iterable: AsyncIterable<T>, callback: () => void): AsyncIterable<T> {
|
|
37
|
+
for await (const item of iterable) {
|
|
38
|
+
callback();
|
|
39
|
+
yield item;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function prometheusClientMiddleware(metrics: IClientCallMetrics): ClientMiddleware {
|
|
44
|
+
return async function* prometheusClientMiddlewareGenerator<Request, Response>(
|
|
45
|
+
call: ClientMiddlewareCall<Request, Response>,
|
|
46
|
+
options: CallOptions,
|
|
47
|
+
): AsyncGenerator<Response, Response | void, undefined> {
|
|
48
|
+
const labels = getLabels(call.method);
|
|
49
|
+
|
|
50
|
+
metrics.started(labels);
|
|
51
|
+
|
|
52
|
+
let settled = false;
|
|
53
|
+
let status: Status = Status.OK;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
let request;
|
|
57
|
+
|
|
58
|
+
if (!call.requestStream) {
|
|
59
|
+
request = call.request;
|
|
60
|
+
} else {
|
|
61
|
+
request = incrementStreamMessagesCounter(call.request, metrics.sent.bind(metrics, labels));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!call.responseStream) {
|
|
65
|
+
const response = yield* call.next(request, options);
|
|
66
|
+
settled = true;
|
|
67
|
+
return response;
|
|
68
|
+
} else {
|
|
69
|
+
yield* incrementStreamMessagesCounter(
|
|
70
|
+
call.next(request, options),
|
|
71
|
+
metrics.received.bind(metrics, labels),
|
|
72
|
+
);
|
|
73
|
+
settled = true;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
settled = true;
|
|
78
|
+
if (err instanceof ClientError) {
|
|
79
|
+
status = err.code;
|
|
80
|
+
} else if (isAbortError(err)) {
|
|
81
|
+
status = Status.CANCELLED;
|
|
82
|
+
} else {
|
|
83
|
+
status = Status.UNKNOWN;
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
} finally {
|
|
87
|
+
if (!settled) {
|
|
88
|
+
status = Status.CANCELLED;
|
|
89
|
+
}
|
|
90
|
+
metrics.handled({ ...labels, code: Status[status] });
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -7,10 +7,16 @@
|
|
|
7
7
|
import * as chai from "chai";
|
|
8
8
|
const expect = chai.expect;
|
|
9
9
|
import { suite, test } from "mocha-typescript";
|
|
10
|
-
import { oneMonthLater } from "./timeutil";
|
|
10
|
+
import { daysBefore, hoursBefore, oneMonthLater } from "./timeutil";
|
|
11
11
|
|
|
12
12
|
@suite()
|
|
13
13
|
export class TimeutilSpec {
|
|
14
|
+
@test
|
|
15
|
+
testDaysBefore() {
|
|
16
|
+
const now = new Date().toISOString();
|
|
17
|
+
expect(daysBefore(now, 2)).to.be.eq(hoursBefore(now, 48));
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
@test
|
|
15
21
|
testTimeutil() {
|
|
16
22
|
// targeting a 1st, 1th of Jan => 1st of Feb
|
|
@@ -39,4 +45,25 @@ export class TimeutilSpec {
|
|
|
39
45
|
const later = oneMonthLater(from.toISOString(), day);
|
|
40
46
|
expect(later, `expected ${later} to be equal ${expectation}`).to.be.equal(expectation.toISOString());
|
|
41
47
|
}
|
|
48
|
+
|
|
49
|
+
@test
|
|
50
|
+
testDaysBefore2() {
|
|
51
|
+
const tests: { date: Date; daysEarlier: number; expectation: string }[] = [
|
|
52
|
+
{
|
|
53
|
+
date: new Date("2021-07-13T00:00:00.000Z"),
|
|
54
|
+
daysEarlier: 365,
|
|
55
|
+
expectation: "2020-07-13T00:00:00.000Z",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
date: new Date("2019-02-01T00:00:00.000Z"),
|
|
59
|
+
daysEarlier: 365,
|
|
60
|
+
expectation: "2018-02-01T00:00:00.000Z",
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
for (const t of tests) {
|
|
65
|
+
const actual = daysBefore(t.date.toISOString(), t.daysEarlier);
|
|
66
|
+
expect(actual).to.equal(t.expectation, `expected ${actual} to be equal ${t.expectation}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
42
69
|
}
|
package/src/util/timeutil.ts
CHANGED
|
@@ -47,6 +47,12 @@ export const orderAsc = (d1: string, d2: string): number => liftDate(d1, d2, (d1
|
|
|
47
47
|
export const liftDate1 = <T>(d1: string, f: (d1: Date) => T): T => f(new Date(d1));
|
|
48
48
|
export const liftDate = <T>(d1: string, d2: string, f: (d1: Date, d2: Date) => T): T => f(new Date(d1), new Date(d2));
|
|
49
49
|
|
|
50
|
+
export function daysBefore(date: string, days: number): string {
|
|
51
|
+
const result = new Date(date);
|
|
52
|
+
result.setDate(result.getDate() - days);
|
|
53
|
+
return result.toISOString();
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
export function hoursBefore(date: string, hours: number): string {
|
|
51
57
|
const result = new Date(date);
|
|
52
58
|
result.setHours(result.getHours() - hours);
|
package/src/workspace-class.ts
CHANGED
|
@@ -246,6 +246,9 @@ export interface WorkspaceInstanceConfiguration {
|
|
|
246
246
|
// desktopIdeImage is the ref of the desktop IDE image this instance uses.
|
|
247
247
|
desktopIdeImage?: string;
|
|
248
248
|
|
|
249
|
+
// desktopIdePluginImage is the ref of the desktop IDE plugin image this instance uses.
|
|
250
|
+
desktopIdePluginImage?: string;
|
|
251
|
+
|
|
249
252
|
// supervisorImage is the ref of the supervisor image this instance uses.
|
|
250
253
|
supervisorImage?: string;
|
|
251
254
|
|