@tomflow/proflow-dev-tunnel 0.1.5 → 0.1.7
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/DOCS.md +27 -0
- package/SETUP.md +7 -0
- package/dist/deployment/adapter.d.ts +111 -115
- package/dist/deployment/adapter.js +295 -265
- package/dist/deployment/descriptor.d.ts +10 -46
- package/dist/deployment/descriptor.js +15 -67
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/package.json +5 -11
- package/proflow.module.json +12 -67
- package/CHANGELOG.md +0 -31
- package/dist/deployment/verification.d.ts +0 -19
- package/dist/deployment/verification.js +0 -2
- package/self-install.mjs +0 -29
|
@@ -1,42 +1,29 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import {
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { writeModuleSharedFacts, } from "@tomflow/proflow-module-contract";
|
|
4
|
+
import { createDevTunnelRuntime } from "../src/resource-adapter.js";
|
|
4
5
|
import { descriptor } from "./descriptor.js";
|
|
5
|
-
const
|
|
6
|
+
const base = {
|
|
6
7
|
contract: "deployment.result.v1",
|
|
7
8
|
ok: true,
|
|
8
9
|
status: "SUCCEEDED",
|
|
9
10
|
moduleRef: descriptor.moduleRef,
|
|
10
11
|
moduleVersion: descriptor.moduleVersion,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
moduleRef: descriptor.moduleRef,
|
|
18
|
-
moduleVersion: descriptor.moduleVersion,
|
|
19
|
-
actionRequired: { action, description },
|
|
20
|
-
});
|
|
21
|
-
export async function readDevTunnelVerificationEvidence(file, publicBaseUrl) {
|
|
22
|
-
if (!file)
|
|
23
|
-
return undefined;
|
|
12
|
+
};
|
|
13
|
+
const processEffect = "Manage the dev-tunnel public ingress process";
|
|
14
|
+
const stateDir = (context) => join(resolve(context.workspaceRoot), ".proflow", "runtime", "external-resources", "dev-tunnel");
|
|
15
|
+
const stateFile = (context) => join(stateDir(context), "setup.json");
|
|
16
|
+
const processFile = (context) => join(stateDir(context), "process.json");
|
|
17
|
+
async function readState(context) {
|
|
24
18
|
try {
|
|
25
|
-
const raw = JSON.parse(await readFile(
|
|
26
|
-
if (raw.contract !== "proflow.dev-tunnel-
|
|
27
|
-
raw.
|
|
28
|
-
raw.
|
|
29
|
-
typeof raw.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
typeof raw.fileRelay.verified !== "boolean" ||
|
|
34
|
-
typeof raw.fileRelay.message !== "string" ||
|
|
35
|
-
typeof raw.errorSemantics !== "object" ||
|
|
36
|
-
raw.errorSemantics === null ||
|
|
37
|
-
typeof raw.errorSemantics.rateLimit429Verified !== "boolean" ||
|
|
38
|
-
typeof raw.errorSemantics.server5xxVerified !== "boolean" ||
|
|
39
|
-
typeof raw.errorSemantics.message !== "string")
|
|
19
|
+
const raw = JSON.parse(await readFile(stateFile(context), "utf8"));
|
|
20
|
+
if (raw.contract !== "proflow.dev-tunnel-setup.v1" ||
|
|
21
|
+
typeof raw.tunnelId !== "string" ||
|
|
22
|
+
!raw.tunnelId ||
|
|
23
|
+
typeof raw.publicBaseUrl !== "string")
|
|
24
|
+
return undefined;
|
|
25
|
+
const url = new URL(raw.publicBaseUrl);
|
|
26
|
+
if (url.protocol !== "https:")
|
|
40
27
|
return undefined;
|
|
41
28
|
return raw;
|
|
42
29
|
}
|
|
@@ -44,264 +31,307 @@ export async function readDevTunnelVerificationEvidence(file, publicBaseUrl) {
|
|
|
44
31
|
return undefined;
|
|
45
32
|
}
|
|
46
33
|
}
|
|
47
|
-
|
|
34
|
+
async function writeState(context, state) {
|
|
35
|
+
await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
|
|
36
|
+
const tmp = `${stateFile(context)}.${process.pid}.tmp`;
|
|
37
|
+
await writeFile(tmp, `${JSON.stringify(state, null, 2)}
|
|
38
|
+
`, { encoding: "utf8", mode: 0o600 });
|
|
39
|
+
await rename(tmp, stateFile(context));
|
|
40
|
+
}
|
|
41
|
+
function setupInput(context) {
|
|
42
|
+
if (typeof context.input !== "object" ||
|
|
43
|
+
context.input === null ||
|
|
44
|
+
Array.isArray(context.input))
|
|
45
|
+
return {};
|
|
46
|
+
const tunnelId = Reflect.get(context.input, "tunnelId");
|
|
47
|
+
const publicBaseUrl = Reflect.get(context.input, "publicBaseUrl");
|
|
48
48
|
return {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}),
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
49
|
+
...(typeof tunnelId === "string" && tunnelId ? { tunnelId } : {}),
|
|
50
|
+
...(typeof publicBaseUrl === "string" && publicBaseUrl
|
|
51
|
+
? { publicBaseUrl }
|
|
52
|
+
: {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function runtime(context, state) {
|
|
56
|
+
return createDevTunnelRuntime({
|
|
57
|
+
...(state
|
|
58
|
+
? { tunnelId: state.tunnelId, publicBaseUrl: state.publicBaseUrl }
|
|
59
|
+
: {}),
|
|
60
|
+
processStateFile: processFile(context),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export const behaviorAdapter = {
|
|
64
|
+
install: async (context) => {
|
|
65
|
+
await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
|
|
66
|
+
const state = await readState(context);
|
|
67
|
+
if (state)
|
|
68
|
+
await writeModuleSharedFacts(context, descriptor.moduleRef, {
|
|
69
|
+
tunnelId: state.tunnelId,
|
|
70
|
+
publicBaseUrl: state.publicBaseUrl,
|
|
71
|
+
});
|
|
72
|
+
return { result: base, observedEffects: [] };
|
|
73
|
+
},
|
|
74
|
+
uninstall: async (context) => {
|
|
75
|
+
const state = await readState(context);
|
|
76
|
+
if (!state)
|
|
77
|
+
return { result: base, observedEffects: [] };
|
|
78
|
+
try {
|
|
79
|
+
const stopped = await runtime(context, state).stop();
|
|
80
|
+
return stopped.state === "STOPPED"
|
|
81
|
+
? { result: base, observedEffects: [processEffect] }
|
|
82
|
+
: {
|
|
83
|
+
result: {
|
|
84
|
+
...base,
|
|
85
|
+
ok: false,
|
|
86
|
+
status: "FAILED",
|
|
87
|
+
error: {
|
|
88
|
+
code: "UNINSTALL_FAILED",
|
|
89
|
+
message: "dev-tunnel stop state is UNKNOWN",
|
|
90
|
+
retryable: true,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
65
93
|
observedEffects: [],
|
|
66
94
|
};
|
|
67
|
-
|
|
68
|
-
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
69
97
|
return {
|
|
70
98
|
result: {
|
|
71
|
-
...
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
...base,
|
|
100
|
+
ok: false,
|
|
101
|
+
status: "FAILED",
|
|
102
|
+
error: {
|
|
103
|
+
code: "UNINSTALL_FAILED",
|
|
104
|
+
message: error instanceof Error
|
|
105
|
+
? error.message
|
|
106
|
+
: "failed to stop dev-tunnel",
|
|
107
|
+
retryable: true,
|
|
108
|
+
},
|
|
81
109
|
},
|
|
82
110
|
observedEffects: [],
|
|
83
111
|
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
observedEffects: [],
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
if ((await input.runtime.loginStatus()) !== "LOGGED_IN") {
|
|
93
|
-
return {
|
|
94
|
-
result: actionRequired("complete-tunnel-login", "Complete the interactive dev-tunnel login before verifying public ingress"),
|
|
95
|
-
observedEffects: [],
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
const publicBaseUrl = input.runtime.publicBaseUrl();
|
|
99
|
-
if (publicBaseUrl === undefined) {
|
|
100
|
-
return {
|
|
101
|
-
result: actionRequired("configure-tunnel", "publicBaseUrl must be configured before public ingress verification"),
|
|
102
|
-
observedEffects: [],
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
const verification = await verifyPublicIngress(publicBaseUrl, {
|
|
106
|
-
...(input.verifyErrorSemantics === undefined
|
|
107
|
-
? {}
|
|
108
|
-
: { verifyErrorSemantics: input.verifyErrorSemantics }),
|
|
109
|
-
...(input.verifyFileRelay === undefined
|
|
110
|
-
? {}
|
|
111
|
-
: { verifyFileRelay: input.verifyFileRelay }),
|
|
112
|
-
});
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
status: async (context) => {
|
|
115
|
+
const state = await readState(context);
|
|
116
|
+
if (!state) {
|
|
113
117
|
return {
|
|
114
|
-
result:
|
|
115
|
-
|
|
116
|
-
: {
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
result: {
|
|
119
|
+
...base,
|
|
120
|
+
data: {
|
|
121
|
+
setupStatus: "ACTION_REQUIRED",
|
|
122
|
+
runtimeStatus: "STOPPED",
|
|
119
123
|
},
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
: [],
|
|
124
|
+
},
|
|
125
|
+
observedEffects: [],
|
|
123
126
|
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
127
|
+
}
|
|
128
|
+
await writeModuleSharedFacts(context, descriptor.moduleRef, {
|
|
129
|
+
tunnelId: state.tunnelId,
|
|
130
|
+
publicBaseUrl: state.publicBaseUrl,
|
|
131
|
+
});
|
|
132
|
+
const rt = runtime(context, state);
|
|
133
|
+
const login = await rt.loginStatus();
|
|
134
|
+
const observed = await rt.status();
|
|
135
|
+
const configured = state !== undefined && login === "LOGGED_IN";
|
|
136
|
+
const runtimeStatus = observed.state === "RUNNING"
|
|
137
|
+
? "RUNNING"
|
|
138
|
+
: observed.state === "STOPPED"
|
|
139
|
+
? "STOPPED"
|
|
140
|
+
: configured
|
|
141
|
+
? "FAILED"
|
|
142
|
+
: "STOPPED";
|
|
143
|
+
return {
|
|
144
|
+
result: {
|
|
145
|
+
...base,
|
|
146
|
+
data: {
|
|
147
|
+
setupStatus: configured
|
|
148
|
+
? "READY"
|
|
149
|
+
: "ACTION_REQUIRED",
|
|
150
|
+
runtimeStatus,
|
|
140
151
|
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
},
|
|
153
|
+
observedEffects: [],
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
setup: async (context) => {
|
|
157
|
+
await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
|
|
158
|
+
const previous = await readState(context);
|
|
159
|
+
const supplied = setupInput(context);
|
|
160
|
+
const candidate = { ...(previous ?? {}), ...supplied };
|
|
161
|
+
const rt = runtime(context, previous);
|
|
162
|
+
const login = await rt.loginStatus();
|
|
163
|
+
if (login !== "LOGGED_IN")
|
|
164
|
+
return {
|
|
165
|
+
result: {
|
|
166
|
+
...base,
|
|
167
|
+
ok: false,
|
|
168
|
+
status: "ACTION_REQUIRED",
|
|
169
|
+
actionRequired: {
|
|
170
|
+
action: "complete-tunnel-login",
|
|
171
|
+
description: "Complete Microsoft Dev Tunnel login, then rerun setup.",
|
|
172
|
+
},
|
|
149
173
|
},
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
174
|
+
observedEffects: [],
|
|
175
|
+
};
|
|
176
|
+
if (typeof candidate.tunnelId !== "string" ||
|
|
177
|
+
typeof candidate.publicBaseUrl !== "string")
|
|
178
|
+
return {
|
|
179
|
+
result: {
|
|
180
|
+
...base,
|
|
181
|
+
ok: false,
|
|
182
|
+
status: "ACTION_REQUIRED",
|
|
183
|
+
actionRequired: {
|
|
184
|
+
action: "select-or-create-tunnel",
|
|
185
|
+
description: "Create or select the persistent Dev Tunnel and provide tunnelId plus publicBaseUrl to this setup step.",
|
|
186
|
+
},
|
|
156
187
|
},
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
188
|
+
observedEffects: [],
|
|
189
|
+
};
|
|
190
|
+
try {
|
|
191
|
+
const url = new URL(candidate.publicBaseUrl);
|
|
192
|
+
if (url.protocol !== "https:")
|
|
193
|
+
throw new TypeError("publicBaseUrl must be HTTPS");
|
|
194
|
+
const state = {
|
|
195
|
+
contract: "proflow.dev-tunnel-setup.v1",
|
|
196
|
+
tunnelId: candidate.tunnelId,
|
|
197
|
+
publicBaseUrl: url.href,
|
|
198
|
+
};
|
|
199
|
+
await writeState(context, state);
|
|
200
|
+
await writeModuleSharedFacts(context, descriptor.moduleRef, {
|
|
201
|
+
tunnelId: state.tunnelId,
|
|
202
|
+
publicBaseUrl: state.publicBaseUrl,
|
|
203
|
+
});
|
|
204
|
+
return { result: base, observedEffects: [] };
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
161
207
|
return {
|
|
162
|
-
result:
|
|
163
|
-
|
|
164
|
-
:
|
|
165
|
-
|
|
166
|
-
|
|
208
|
+
result: {
|
|
209
|
+
...base,
|
|
210
|
+
ok: false,
|
|
211
|
+
status: "ACTION_REQUIRED",
|
|
212
|
+
actionRequired: {
|
|
213
|
+
action: "correct-tunnel-facts",
|
|
214
|
+
description: error instanceof Error
|
|
215
|
+
? error.message
|
|
216
|
+
: "Tunnel setup facts are invalid",
|
|
167
217
|
},
|
|
218
|
+
},
|
|
168
219
|
observedEffects: [],
|
|
169
220
|
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return {
|
|
180
|
-
result: actionRequired("complete-tunnel-login", "Complete the interactive dev-tunnel login before starting the tunnel"),
|
|
181
|
-
observedEffects: [],
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
try {
|
|
185
|
-
const observation = await input.runtime.start();
|
|
186
|
-
return {
|
|
187
|
-
result: success(observation),
|
|
188
|
-
observedEffects: ["Manage the dev-tunnel public ingress process"],
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
return {
|
|
193
|
-
result: actionRequired("start-tunnel", error instanceof Error
|
|
194
|
-
? error.message
|
|
195
|
-
: "failed to start the dev-tunnel process"),
|
|
196
|
-
observedEffects: [],
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
},
|
|
200
|
-
stop: async () => {
|
|
201
|
-
if (!input) {
|
|
202
|
-
return {
|
|
203
|
-
result: actionRequired("configure-tunnel", "No bound dev-tunnel resource to stop"),
|
|
204
|
-
observedEffects: [],
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
const stopped = await input.runtime.stop();
|
|
208
|
-
if (stopped.state === "STOPPED") {
|
|
209
|
-
return {
|
|
210
|
-
result: success(),
|
|
211
|
-
observedEffects: ["Manage the dev-tunnel public ingress process"],
|
|
212
|
-
};
|
|
213
|
-
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
docs: async (_context) => ({
|
|
224
|
+
result: { ...base, data: { docs: "DOCS.md", setup: "SETUP.md" } },
|
|
225
|
+
observedEffects: [],
|
|
226
|
+
}),
|
|
227
|
+
start: async (context) => {
|
|
228
|
+
const state = await readState(context);
|
|
229
|
+
if (!state)
|
|
214
230
|
return {
|
|
215
|
-
result:
|
|
231
|
+
result: {
|
|
232
|
+
...base,
|
|
233
|
+
ok: false,
|
|
234
|
+
status: "FAILED",
|
|
235
|
+
error: {
|
|
236
|
+
code: "START_FAILED",
|
|
237
|
+
message: "dev-tunnel setup is not READY",
|
|
238
|
+
retryable: true,
|
|
239
|
+
},
|
|
240
|
+
},
|
|
216
241
|
observedEffects: [],
|
|
217
242
|
};
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (!input) {
|
|
221
|
-
return {
|
|
222
|
-
// No bound tunnel means there is no managed external resource to
|
|
223
|
-
// stop. Whole-instance uninstall must remain idempotent.
|
|
224
|
-
result: success(),
|
|
225
|
-
observedEffects: [],
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
const stopped = await input.runtime.stop();
|
|
229
|
-
if (stopped.state === "STOPPED") {
|
|
230
|
-
return {
|
|
231
|
-
result: success(),
|
|
232
|
-
observedEffects: ["Manage the dev-tunnel public ingress process"],
|
|
233
|
-
};
|
|
234
|
-
}
|
|
243
|
+
const rt = runtime(context, state);
|
|
244
|
+
if ((await rt.loginStatus()) !== "LOGGED_IN")
|
|
235
245
|
return {
|
|
236
|
-
result:
|
|
246
|
+
result: {
|
|
247
|
+
...base,
|
|
248
|
+
ok: false,
|
|
249
|
+
status: "FAILED",
|
|
250
|
+
error: {
|
|
251
|
+
code: "START_FAILED",
|
|
252
|
+
message: "Microsoft Dev Tunnel login is not ready",
|
|
253
|
+
retryable: true,
|
|
254
|
+
},
|
|
255
|
+
},
|
|
237
256
|
observedEffects: [],
|
|
238
257
|
};
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
result:
|
|
244
|
-
observedEffects: [],
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
258
|
+
try {
|
|
259
|
+
const observed = await rt.start();
|
|
260
|
+
return observed.state === "RUNNING"
|
|
261
|
+
? {
|
|
262
|
+
result: { ...base, data: observed },
|
|
263
|
+
observedEffects: [processEffect],
|
|
264
|
+
}
|
|
265
|
+
: {
|
|
266
|
+
result: {
|
|
267
|
+
...base,
|
|
268
|
+
ok: false,
|
|
269
|
+
status: "FAILED",
|
|
270
|
+
error: {
|
|
271
|
+
code: "START_FAILED",
|
|
272
|
+
message: "dev-tunnel did not reach RUNNING",
|
|
273
|
+
retryable: true,
|
|
274
|
+
},
|
|
275
|
+
},
|
|
257
276
|
observedEffects: [],
|
|
258
277
|
};
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
:
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
return {
|
|
281
|
+
result: {
|
|
282
|
+
...base,
|
|
283
|
+
ok: false,
|
|
284
|
+
status: "FAILED",
|
|
285
|
+
error: {
|
|
286
|
+
code: "START_FAILED",
|
|
287
|
+
message: error instanceof Error
|
|
288
|
+
? error.message
|
|
289
|
+
: "failed to start dev-tunnel",
|
|
290
|
+
retryable: true,
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
observedEffects: [],
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
stop: async (context) => {
|
|
298
|
+
const state = await readState(context);
|
|
299
|
+
if (!state)
|
|
300
|
+
return { result: base, observedEffects: [] };
|
|
301
|
+
try {
|
|
302
|
+
const observed = await runtime(context, state).stop();
|
|
303
|
+
return observed.state === "STOPPED"
|
|
304
|
+
? { result: base, observedEffects: [processEffect] }
|
|
305
|
+
: {
|
|
306
|
+
result: {
|
|
307
|
+
...base,
|
|
308
|
+
ok: false,
|
|
309
|
+
status: "FAILED",
|
|
310
|
+
error: {
|
|
311
|
+
code: "STOP_FAILED",
|
|
312
|
+
message: "dev-tunnel stop state is UNKNOWN",
|
|
313
|
+
retryable: true,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
272
316
|
observedEffects: [],
|
|
273
317
|
};
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
processStateFile: join(input.workspaceRoot, ".proflow", "runtime", "external-resources", "dev-tunnel", "process.json"),
|
|
295
|
-
}),
|
|
296
|
-
verifyFileRelay: async () => (await readEvidence())?.fileRelay ?? {
|
|
297
|
-
verified: false,
|
|
298
|
-
message: "dev-tunnel verification evidence is missing, stale, or invalid",
|
|
299
|
-
},
|
|
300
|
-
verifyErrorSemantics: async () => (await readEvidence())?.errorSemantics ?? {
|
|
301
|
-
rateLimit429Verified: false,
|
|
302
|
-
server5xxVerified: false,
|
|
303
|
-
message: "dev-tunnel verification evidence is missing, stale, or invalid",
|
|
304
|
-
},
|
|
305
|
-
}),
|
|
306
|
-
};
|
|
307
|
-
}
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
return {
|
|
321
|
+
result: {
|
|
322
|
+
...base,
|
|
323
|
+
ok: false,
|
|
324
|
+
status: "FAILED",
|
|
325
|
+
error: {
|
|
326
|
+
code: "STOP_FAILED",
|
|
327
|
+
message: error instanceof Error
|
|
328
|
+
? error.message
|
|
329
|
+
: "failed to stop dev-tunnel",
|
|
330
|
+
retryable: true,
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
observedEffects: [],
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
};
|