@rigkit/provider-freestyle 0.2.3 → 0.2.4
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 +9 -3
- package/package.json +8 -5
- package/src/host-auth.test.ts +375 -0
- package/src/host-auth.ts +591 -0
- package/src/index.ts +37 -59
- package/src/provider.test.ts +171 -113
- package/src/provider.ts +106 -380
- package/src/terminal-session.test.ts +42 -2
- package/src/terminal-session.ts +633 -308
- package/src/version.ts +1 -1
package/src/index.ts
CHANGED
|
@@ -3,30 +3,37 @@ import {
|
|
|
3
3
|
type WorkflowProviderDefinition,
|
|
4
4
|
} from "@rigkit/sdk";
|
|
5
5
|
import type { BaseProviderPlugin } from "@rigkit/engine";
|
|
6
|
-
import type { WorkflowProviderController } from "@rigkit/engine";
|
|
7
|
-
import { Freestyle } from "freestyle";
|
|
8
6
|
import * as z from "zod/v4-mini";
|
|
9
7
|
import { freestyleIdentityId, freestyleToken, freestyleTokenId } from "./auth.ts";
|
|
8
|
+
import {
|
|
9
|
+
createFreestyleAuthenticatedClient,
|
|
10
|
+
createFreestyleProxyFetch,
|
|
11
|
+
type FreestyleProviderAuthConfig,
|
|
12
|
+
} from "./host-auth.ts";
|
|
10
13
|
import {
|
|
11
14
|
FREESTYLE_PROVIDER_ID,
|
|
12
15
|
FREESTYLE_TERMINAL_PROVIDER_ID,
|
|
13
16
|
createFreestyleTerminalController,
|
|
14
17
|
createFreestyleWorkflowProvider,
|
|
15
|
-
isFreestyleVmSnapshotRef,
|
|
16
18
|
} from "./provider.ts";
|
|
17
19
|
import type { FreestyleRuntime, FreestyleTerminalRuntime } from "./provider.ts";
|
|
18
|
-
import { createFreestyleStore } from "./store.ts";
|
|
19
20
|
|
|
20
21
|
const freestyleProviderConfigSchema = z.object({
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
auth: z.optional(z.object({
|
|
23
|
+
apiKey: z.optional(z.string().check(z.minLength(1))),
|
|
24
|
+
profile: z.optional(z.string().check(z.minLength(1))),
|
|
25
|
+
teamId: z.optional(z.string().check(z.minLength(1))),
|
|
26
|
+
apiUrl: z.optional(z.string().check(z.minLength(1))),
|
|
27
|
+
dashboardUrl: z.optional(z.string().check(z.minLength(1))),
|
|
28
|
+
stackApiUrl: z.optional(z.string().check(z.minLength(1))),
|
|
29
|
+
stackAppUrl: z.optional(z.string().check(z.minLength(1))),
|
|
30
|
+
stackProjectId: z.optional(z.string().check(z.minLength(1))),
|
|
31
|
+
stackPublishableClientKey: z.optional(z.string().check(z.minLength(1))),
|
|
32
|
+
})),
|
|
27
33
|
});
|
|
28
34
|
|
|
29
35
|
export type FreestyleProviderConfig = z.output<typeof freestyleProviderConfigSchema>;
|
|
36
|
+
export type { FreestyleProviderAuthConfig };
|
|
30
37
|
|
|
31
38
|
export type FreestyleProviderDefinition = WorkflowProviderDefinition<
|
|
32
39
|
typeof FREESTYLE_PROVIDER_ID,
|
|
@@ -41,7 +48,7 @@ export type FreestyleTerminalProviderDefinition = WorkflowProviderDefinition<
|
|
|
41
48
|
>;
|
|
42
49
|
|
|
43
50
|
export function provider(
|
|
44
|
-
config: FreestyleProviderDefinition["config"],
|
|
51
|
+
config: FreestyleProviderDefinition["config"] = {},
|
|
45
52
|
): FreestyleProviderDefinition {
|
|
46
53
|
return defineProvider(FREESTYLE_PROVIDER_ID, config, freestyleProviderPlugin);
|
|
47
54
|
}
|
|
@@ -59,50 +66,18 @@ export const defineFreestyleProvider = provider;
|
|
|
59
66
|
|
|
60
67
|
export const freestyleProviderPlugin: BaseProviderPlugin = {
|
|
61
68
|
providerId: FREESTYLE_PROVIDER_ID,
|
|
62
|
-
createProvider({ provider,
|
|
69
|
+
async createProvider({ provider, hostStorage, local }) {
|
|
63
70
|
const config = parseFreestyleProviderConfig(provider.config);
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const savedIdentity = store.getIdentity();
|
|
75
|
-
if (savedIdentity) {
|
|
76
|
-
return createFreestyleWorkflowProvider({
|
|
77
|
-
apiKey,
|
|
78
|
-
identityId: savedIdentity.identityId,
|
|
79
|
-
token: savedIdentity.token,
|
|
80
|
-
vm,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const client = new Freestyle({ apiKey });
|
|
85
|
-
const { identity, identityId } = await client.identities.create();
|
|
86
|
-
const { token, tokenId } = await identity.tokens.create();
|
|
87
|
-
const createdIdentity = store.saveIdentity({
|
|
88
|
-
identityId: freestyleIdentityId(identityId),
|
|
89
|
-
tokenId: freestyleTokenId(tokenId),
|
|
90
|
-
token: freestyleToken(token),
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
return createFreestyleWorkflowProvider({
|
|
94
|
-
apiKey,
|
|
95
|
-
identityId: createdIdentity.identityId,
|
|
96
|
-
token: createdIdentity.token,
|
|
97
|
-
vm,
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
providerId: FREESTYLE_PROVIDER_ID,
|
|
103
|
-
runtime: async (context) => await (await load()).runtime(context),
|
|
104
|
-
validateArtifact: (ref) => isFreestyleVmSnapshotRef(ref),
|
|
105
|
-
} satisfies WorkflowProviderController<FreestyleRuntime>;
|
|
71
|
+
const authenticated = await createFreestyleAuthenticatedClient({
|
|
72
|
+
auth: config.auth,
|
|
73
|
+
hostStorage,
|
|
74
|
+
local,
|
|
75
|
+
});
|
|
76
|
+
return createFreestyleWorkflowProvider({
|
|
77
|
+
client: authenticated.client,
|
|
78
|
+
identityId: authenticated.identityId,
|
|
79
|
+
token: authenticated.token,
|
|
80
|
+
});
|
|
106
81
|
},
|
|
107
82
|
};
|
|
108
83
|
|
|
@@ -113,6 +88,10 @@ export const freestyleTerminalPlugin: BaseProviderPlugin = {
|
|
|
113
88
|
},
|
|
114
89
|
};
|
|
115
90
|
|
|
91
|
+
export {
|
|
92
|
+
createFreestyleAuthenticatedClient,
|
|
93
|
+
createFreestyleProxyFetch,
|
|
94
|
+
} from "./host-auth.ts";
|
|
116
95
|
export {
|
|
117
96
|
freestyleIdentityId,
|
|
118
97
|
freestyleToken,
|
|
@@ -124,24 +103,23 @@ export {
|
|
|
124
103
|
export {
|
|
125
104
|
FREESTYLE_PROVIDER_ID,
|
|
126
105
|
FREESTYLE_TERMINAL_PROVIDER_ID,
|
|
127
|
-
createFreestyleProvider,
|
|
128
106
|
createFreestyleTerminalController,
|
|
129
107
|
createFreestyleWorkflowController,
|
|
130
108
|
createFreestyleWorkflowProvider,
|
|
131
|
-
isFreestyleVmSnapshotRef,
|
|
132
109
|
} from "./provider.ts";
|
|
133
110
|
export { createFreestyleStore } from "./store.ts";
|
|
134
111
|
export { createFreestyleTerminalSession } from "./terminal-session.ts";
|
|
135
112
|
export { RIGKIT_PROVIDER_FREESTYLE_VERSION } from "./version.ts";
|
|
113
|
+
export { Freestyle, VmBaseImage, VmSpec, VmWith, VmWithInstance } from "freestyle";
|
|
114
|
+
export type { CreateVmOptions } from "freestyle";
|
|
136
115
|
export type {
|
|
137
116
|
FreestyleCmuxSshOptions,
|
|
138
117
|
FreestyleCmuxSshOptionsInput,
|
|
139
118
|
FreestyleRuntime,
|
|
119
|
+
FreestyleSdkVm,
|
|
120
|
+
FreestyleSshInput,
|
|
140
121
|
FreestyleTerminalRuntime,
|
|
141
122
|
FreestyleVscodeUrlOptions,
|
|
142
|
-
FreestyleVmConfig,
|
|
143
|
-
FreestyleVmRuntime,
|
|
144
|
-
FreestyleVmSnapshotRef,
|
|
145
123
|
} from "./provider.ts";
|
|
146
124
|
export type { FreestyleGitRelationship, FreestyleIdentity } from "./store.ts";
|
|
147
125
|
|
package/src/provider.test.ts
CHANGED
|
@@ -1,82 +1,61 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
test("allows callers to override HOME explicitly", () => {
|
|
18
|
-
const wrapped = wrapCommand("pwd", {
|
|
19
|
-
env: { HOME: "/workspace/home" },
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
expect(wrapped).toContain("export HOME=${HOME:-/root}");
|
|
23
|
-
expect(wrapped).toContain("export HOME='\\''/workspace/home'\\''");
|
|
24
|
-
});
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { Freestyle } from "freestyle";
|
|
3
|
+
import type { ProviderInteractionSession, ProviderRuntimeContext } from "@rigkit/engine";
|
|
4
|
+
import { freestyleIdentityId, freestyleToken } from "./auth.ts";
|
|
5
|
+
import {
|
|
6
|
+
buildInteractiveSshCommand,
|
|
7
|
+
createFreestyleTerminalController,
|
|
8
|
+
createFreestyleWorkflowController,
|
|
9
|
+
} from "./provider.ts";
|
|
10
|
+
|
|
11
|
+
const previousFetch = globalThis.fetch;
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
globalThis.fetch = previousFetch;
|
|
15
|
+
});
|
|
25
16
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
17
|
+
describe("Freestyle provider host adapters", () => {
|
|
18
|
+
test("creates SSH options and grants VM access internally", async () => {
|
|
19
|
+
const requests: string[] = [];
|
|
20
|
+
globalThis.fetch = (async (resource, init) => {
|
|
21
|
+
requests.push(`${init?.method ?? "GET"} ${String(resource)}`);
|
|
22
|
+
return Response.json({});
|
|
23
|
+
}) as typeof fetch;
|
|
24
|
+
|
|
25
|
+
const runtime = await createFreestyleWorkflowController({
|
|
26
|
+
client: new Freestyle({ apiKey: "test-key" }),
|
|
27
|
+
identityId: freestyleIdentityId("identity-stream"),
|
|
28
|
+
token: freestyleToken("token"),
|
|
29
|
+
}).runtime(providerContext());
|
|
30
|
+
|
|
31
|
+
await expect(runtime.createSSHOptions({ vmId: "vm-stream" })).resolves.toEqual({
|
|
32
|
+
kind: "ssh",
|
|
33
|
+
host: "vm-ssh.freestyle.sh",
|
|
34
|
+
username: "vm-stream+root",
|
|
35
|
+
auth: { type: "token", token: "token" },
|
|
36
|
+
command: "ssh vm-stream+root:token@vm-ssh.freestyle.sh",
|
|
39
37
|
});
|
|
40
|
-
|
|
41
|
-
expect(result.stdout).toBe("ready\n");
|
|
42
|
-
expect(chunks).toEqual([{ stream: "stdout", data: "ready\n" }]);
|
|
43
|
-
expect(events).toEqual([
|
|
44
|
-
{
|
|
45
|
-
type: "command.started",
|
|
46
|
-
nodePath: "workflow.step",
|
|
47
|
-
commandName: "stream command",
|
|
48
|
-
command: "printf ready",
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
type: "command.output",
|
|
52
|
-
nodePath: "workflow.step",
|
|
53
|
-
commandName: "stream command",
|
|
54
|
-
stream: "stdout",
|
|
55
|
-
data: "ready\n",
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
type: "command.completed",
|
|
59
|
-
nodePath: "workflow.step",
|
|
60
|
-
commandName: "stream command",
|
|
61
|
-
exitCode: 0,
|
|
62
|
-
},
|
|
63
|
-
]);
|
|
38
|
+
expect(requests).toContain("POST https://api.freestyle.sh/identity/v1/identities/identity-stream/permissions/vm/vm-stream");
|
|
64
39
|
});
|
|
65
40
|
|
|
66
41
|
test("creates cmux ssh options with Freestyle-owned ssh settings", async () => {
|
|
67
|
-
|
|
68
|
-
const controller = createFreestyleWorkflowController(provider);
|
|
69
|
-
const runtime = await controller.runtime(providerContext([]));
|
|
70
|
-
const vm = runtime.vms.fromId("vm-stream");
|
|
42
|
+
globalThis.fetch = (async () => Response.json({})) as unknown as typeof fetch;
|
|
71
43
|
|
|
72
|
-
const
|
|
44
|
+
const runtime = await createFreestyleWorkflowController({
|
|
45
|
+
client: new Freestyle({ apiKey: "test-key" }),
|
|
46
|
+
identityId: freestyleIdentityId("identity-stream"),
|
|
47
|
+
token: freestyleToken("token"),
|
|
48
|
+
}).runtime(providerContext());
|
|
49
|
+
|
|
50
|
+
const ssh = await runtime.cmux.createSshOptions({
|
|
51
|
+
vmId: "vm-stream",
|
|
73
52
|
sshOptions: ["ServerAliveInterval=15"],
|
|
74
53
|
skipDaemonBootstrap: true,
|
|
75
54
|
});
|
|
76
55
|
|
|
77
56
|
expect(ssh).toEqual({
|
|
78
57
|
kind: "ssh",
|
|
79
|
-
destination: "root,token@
|
|
58
|
+
destination: "vm-stream+root,token@vm-ssh.freestyle.sh",
|
|
80
59
|
skipDaemonBootstrap: true,
|
|
81
60
|
sshOptions: [
|
|
82
61
|
"StrictHostKeyChecking=no",
|
|
@@ -90,71 +69,151 @@ describe("Freestyle provider command wrapper", () => {
|
|
|
90
69
|
});
|
|
91
70
|
});
|
|
92
71
|
|
|
72
|
+
test("treats existing VM permissions as idempotent for cmux ssh options", async () => {
|
|
73
|
+
const calls: string[] = [];
|
|
74
|
+
const runtime = await createFreestyleWorkflowController({
|
|
75
|
+
client: {
|
|
76
|
+
identities: {
|
|
77
|
+
ref: () => ({
|
|
78
|
+
permissions: {
|
|
79
|
+
vms: {
|
|
80
|
+
grant: async () => {
|
|
81
|
+
calls.push("grant");
|
|
82
|
+
throw new Error("PERMISSION_ALREADY_EXISTS: Permission already exists");
|
|
83
|
+
},
|
|
84
|
+
update: async () => {
|
|
85
|
+
calls.push("update");
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
},
|
|
91
|
+
} as unknown as Freestyle,
|
|
92
|
+
identityId: freestyleIdentityId("identity-stream"),
|
|
93
|
+
token: freestyleToken("token"),
|
|
94
|
+
}).runtime(providerContext());
|
|
95
|
+
|
|
96
|
+
await expect(runtime.cmux.createSshOptions({ vmId: "vm-stream" })).resolves.toMatchObject({
|
|
97
|
+
destination: "vm-stream+root,token@vm-ssh.freestyle.sh",
|
|
98
|
+
});
|
|
99
|
+
expect(calls).toEqual(["grant", "update"]);
|
|
100
|
+
});
|
|
101
|
+
|
|
93
102
|
test("creates VS Code URLs using the Freestyle ssh authority", async () => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const runtime = await
|
|
97
|
-
|
|
103
|
+
globalThis.fetch = (async () => Response.json({})) as unknown as typeof fetch;
|
|
104
|
+
|
|
105
|
+
const runtime = await createFreestyleWorkflowController({
|
|
106
|
+
client: new Freestyle({ apiKey: "test-key" }),
|
|
107
|
+
identityId: freestyleIdentityId("identity-stream"),
|
|
108
|
+
token: freestyleToken("token"),
|
|
109
|
+
}).runtime(providerContext());
|
|
98
110
|
|
|
99
|
-
const url = await runtime.vscode.createUrl(vm,
|
|
111
|
+
const url = await runtime.vscode.createUrl({ vmId: "vm-stream", cwd: "/workspace/site" });
|
|
100
112
|
|
|
101
113
|
expect(url).toBe(
|
|
102
|
-
"vscode://vscode-remote/ssh-remote+
|
|
114
|
+
"vscode://vscode-remote/ssh-remote+vm-stream%2Broot%3Atoken%40vm-ssh.freestyle.sh/workspace/site?windowId=_blank",
|
|
103
115
|
);
|
|
104
116
|
});
|
|
105
|
-
});
|
|
106
117
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
host: "localhost",
|
|
110
|
-
username: "root",
|
|
111
|
-
auth: { type: "token", token: "token" },
|
|
112
|
-
command: "ssh vm-stream",
|
|
113
|
-
};
|
|
118
|
+
test("honors explicit SSH users", async () => {
|
|
119
|
+
globalThis.fetch = (async () => Response.json({})) as unknown as typeof fetch;
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
const runtime = await createFreestyleWorkflowController({
|
|
122
|
+
client: new Freestyle({ apiKey: "test-key" }),
|
|
123
|
+
identityId: freestyleIdentityId("identity-stream"),
|
|
124
|
+
token: freestyleToken("token"),
|
|
125
|
+
}).runtime(providerContext());
|
|
117
126
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
await expect(runtime.createSSHOptions({ vmId: "vm-stream", user: "ubuntu" })).resolves.toMatchObject({
|
|
128
|
+
username: "vm-stream+ubuntu",
|
|
129
|
+
command: "ssh vm-stream+ubuntu:token@vm-ssh.freestyle.sh",
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("runs terminal commands as SSH remote commands instead of typed startup input", async () => {
|
|
134
|
+
let html = "";
|
|
135
|
+
const runtime = await createFreestyleTerminalController().runtime({
|
|
136
|
+
...providerContext(),
|
|
137
|
+
interaction: {
|
|
138
|
+
present: async <Result>(session: ProviderInteractionSession<Result>) => {
|
|
139
|
+
const response = await fetch(session.url);
|
|
140
|
+
html = await response.text();
|
|
141
|
+
session.stop();
|
|
142
|
+
return { finished: true } as Result;
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
121
146
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
147
|
+
await expect(runtime.open("GitHub auth", {
|
|
148
|
+
ssh: {
|
|
149
|
+
kind: "ssh",
|
|
150
|
+
host: "vm-ssh.freestyle.sh",
|
|
151
|
+
username: "vm-stream+root",
|
|
152
|
+
auth: { type: "token", token: "token" },
|
|
153
|
+
command: "ssh vm-stream+root:token@vm-ssh.freestyle.sh",
|
|
154
|
+
},
|
|
155
|
+
command: "gh auth login --hostname github.com",
|
|
156
|
+
})).resolves.toEqual({ finished: true });
|
|
125
157
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
158
|
+
expect(html).toContain("gh auth login --hostname github.com");
|
|
159
|
+
expect(html).toContain("const startupInput = null;");
|
|
160
|
+
expect(html).toContain("const canFinishWhileRunning = false;");
|
|
161
|
+
});
|
|
130
162
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
163
|
+
test("can keep an SSH terminal open after a successful remote command", () => {
|
|
164
|
+
const command = buildInteractiveSshCommand(
|
|
165
|
+
{
|
|
166
|
+
kind: "ssh",
|
|
167
|
+
host: "vm-ssh.freestyle.sh",
|
|
168
|
+
username: "vm-stream+root",
|
|
169
|
+
auth: { type: "token", token: "token" },
|
|
170
|
+
command: "ssh vm-stream+root:token@vm-ssh.freestyle.sh",
|
|
171
|
+
},
|
|
172
|
+
"gh auth status -h github.com",
|
|
173
|
+
{ keepOpenAfterCommand: true },
|
|
174
|
+
);
|
|
134
175
|
|
|
135
|
-
|
|
176
|
+
expect(command).toContain("gh auth status -h github.com");
|
|
177
|
+
expect(command).toContain("status=$?");
|
|
178
|
+
expect(command).toContain('if [ "$status" -ne 0 ]; then exit "$status"; fi');
|
|
179
|
+
expect(command).toContain('exec "${SHELL:-/bin/bash}" -l');
|
|
180
|
+
});
|
|
136
181
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
182
|
+
test("allows finishing while a keep-open SSH command is running", async () => {
|
|
183
|
+
let html = "";
|
|
184
|
+
const runtime = await createFreestyleTerminalController().runtime({
|
|
185
|
+
...providerContext(),
|
|
186
|
+
interaction: {
|
|
187
|
+
present: async <Result>(session: ProviderInteractionSession<Result>) => {
|
|
188
|
+
const response = await fetch(session.url);
|
|
189
|
+
html = await response.text();
|
|
190
|
+
session.stop();
|
|
191
|
+
return { finished: true } as Result;
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
});
|
|
140
195
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
196
|
+
await runtime.open("GitHub auth", {
|
|
197
|
+
ssh: {
|
|
198
|
+
kind: "ssh",
|
|
199
|
+
host: "vm-ssh.freestyle.sh",
|
|
200
|
+
username: "vm-stream+root",
|
|
201
|
+
auth: { type: "token", token: "token" },
|
|
202
|
+
command: "ssh vm-stream+root:token@vm-ssh.freestyle.sh",
|
|
203
|
+
},
|
|
204
|
+
command: "gh auth login --hostname github.com",
|
|
205
|
+
keepOpenAfterCommand: true,
|
|
206
|
+
});
|
|
144
207
|
|
|
145
|
-
|
|
146
|
-
}
|
|
208
|
+
expect(html).toContain("const canFinishWhileRunning = true;");
|
|
209
|
+
});
|
|
210
|
+
});
|
|
147
211
|
|
|
148
|
-
function providerContext(
|
|
149
|
-
events: WorkflowEvent[],
|
|
150
|
-
local: Partial<ProviderRuntimeContext["local"]> = {},
|
|
151
|
-
): ProviderRuntimeContext {
|
|
212
|
+
function providerContext(): ProviderRuntimeContext {
|
|
152
213
|
return {
|
|
153
214
|
workflow: "workflow",
|
|
154
215
|
nodePath: "workflow.step",
|
|
155
|
-
emit: (
|
|
156
|
-
events.push(event);
|
|
157
|
-
},
|
|
216
|
+
emit: () => {},
|
|
158
217
|
interaction: {
|
|
159
218
|
present: async () => {
|
|
160
219
|
throw new Error("unexpected interaction");
|
|
@@ -162,7 +221,6 @@ function providerContext(
|
|
|
162
221
|
},
|
|
163
222
|
local: {
|
|
164
223
|
open: async () => {},
|
|
165
|
-
...local,
|
|
166
224
|
},
|
|
167
225
|
metadata: () => {},
|
|
168
226
|
};
|