@tomflow/proflow-dev-tunnel 0.1.21 → 0.1.23

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @tomflow/proflow-dev-tunnel
2
2
 
3
+ ## 0.1.23
4
+
5
+ ### Patch Changes
6
+
7
+ - Require live HTTPS ingress readiness before dev-tunnel start reports RUNNING, and clean up the host when readiness fails.
8
+
9
+ ## 0.1.22
10
+
11
+ ### Patch Changes
12
+
13
+ - Accept Microsoft Dev Tunnel canonical cluster suffixes while preserving the deterministic workspace Tunnel identity.
14
+
3
15
  ## 0.1.21
4
16
 
5
17
  ### Patch Changes
@@ -28,7 +28,7 @@ export declare function createDevTunnelBehaviorAdapter(dependencies?: {
28
28
  readonly ok: true;
29
29
  readonly status: "SUCCEEDED";
30
30
  readonly moduleRef: "dev-tunnel";
31
- readonly moduleVersion: "0.1.21";
31
+ readonly moduleVersion: "0.1.23";
32
32
  };
33
33
  observedEffects: never[];
34
34
  }>;
@@ -38,14 +38,14 @@ export declare function createDevTunnelBehaviorAdapter(dependencies?: {
38
38
  readonly ok: true;
39
39
  readonly status: "SUCCEEDED";
40
40
  readonly moduleRef: "dev-tunnel";
41
- readonly moduleVersion: "0.1.21";
41
+ readonly moduleVersion: "0.1.23";
42
42
  };
43
43
  observedEffects: string[];
44
44
  } | {
45
45
  result: {
46
46
  contract: "deployment.result.v1";
47
47
  moduleRef: "dev-tunnel";
48
- moduleVersion: "0.1.21";
48
+ moduleVersion: "0.1.23";
49
49
  ok: false;
50
50
  status: "FAILED";
51
51
  error: {
@@ -62,7 +62,7 @@ export declare function createDevTunnelBehaviorAdapter(dependencies?: {
62
62
  ok: true;
63
63
  status: "SUCCEEDED";
64
64
  moduleRef: "dev-tunnel";
65
- moduleVersion: "0.1.21";
65
+ moduleVersion: "0.1.23";
66
66
  data: {
67
67
  setupStatus: "ACTION_REQUIRED" | "READY";
68
68
  runtimeStatus: "FAILED" | "RUNNING" | "STOPPED";
@@ -89,67 +89,67 @@ export declare function createDevTunnelBehaviorAdapter(dependencies?: {
89
89
  ok: true;
90
90
  status: "SUCCEEDED";
91
91
  moduleRef: "dev-tunnel";
92
- moduleVersion: "0.1.21";
92
+ moduleVersion: "0.1.23";
93
93
  data: {
94
94
  docs: string;
95
95
  };
96
96
  };
97
97
  observedEffects: never[];
98
98
  }>;
99
- readonly start: (context: ModuleCommandContext) => Promise<{
99
+ readonly stop: (context: ModuleCommandContext) => Promise<{
100
+ result: {
101
+ readonly contract: "deployment.result.v1";
102
+ readonly ok: true;
103
+ readonly status: "SUCCEEDED";
104
+ readonly moduleRef: "dev-tunnel";
105
+ readonly moduleVersion: "0.1.23";
106
+ };
107
+ observedEffects: string[];
108
+ } | {
100
109
  result: {
101
110
  contract: "deployment.result.v1";
102
111
  moduleRef: "dev-tunnel";
103
- moduleVersion: "0.1.21";
112
+ moduleVersion: "0.1.23";
104
113
  ok: false;
105
114
  status: "FAILED";
106
115
  error: {
107
- code: "START_FAILED";
116
+ code: "STOP_FAILED";
108
117
  message: string;
109
118
  retryable: boolean;
110
119
  };
111
120
  };
112
121
  observedEffects: never[];
113
- } | {
114
- result: {
115
- contract: "deployment.result.v1";
116
- ok: true;
117
- status: "SUCCEEDED";
118
- moduleRef: "dev-tunnel";
119
- moduleVersion: "0.1.21";
120
- data: import("../src/resource-adapter.ts").DevTunnelObservation;
121
- };
122
- observedEffects: string[];
123
122
  }>;
124
- readonly stop: (context: ModuleCommandContext) => Promise<{
125
- result: {
126
- readonly contract: "deployment.result.v1";
127
- readonly ok: true;
128
- readonly status: "SUCCEEDED";
129
- readonly moduleRef: "dev-tunnel";
130
- readonly moduleVersion: "0.1.21";
131
- };
132
- observedEffects: string[];
133
- } | {
123
+ readonly start: (context: ModuleCommandContext) => Promise<{
134
124
  result: {
135
125
  contract: "deployment.result.v1";
136
126
  moduleRef: "dev-tunnel";
137
- moduleVersion: "0.1.21";
127
+ moduleVersion: "0.1.23";
138
128
  ok: false;
139
129
  status: "FAILED";
140
130
  error: {
141
- code: "STOP_FAILED";
131
+ code: "START_FAILED";
142
132
  message: string;
143
133
  retryable: boolean;
144
134
  };
145
135
  };
146
136
  observedEffects: never[];
137
+ } | {
138
+ result: {
139
+ contract: "deployment.result.v1";
140
+ ok: true;
141
+ status: "SUCCEEDED";
142
+ moduleRef: "dev-tunnel";
143
+ moduleVersion: "0.1.23";
144
+ data: import("../src/resource-adapter.ts").DevTunnelObservation;
145
+ };
146
+ observedEffects: string[];
147
147
  }>;
148
148
  readonly setup: (context: ModuleCommandContext) => Promise<{
149
149
  result: {
150
150
  contract: "deployment.result.v1";
151
151
  moduleRef: "dev-tunnel";
152
- moduleVersion: "0.1.21";
152
+ moduleVersion: "0.1.23";
153
153
  ok: false;
154
154
  status: "FAILED";
155
155
  error: {
@@ -165,7 +165,7 @@ export declare function createDevTunnelBehaviorAdapter(dependencies?: {
165
165
  ok: true;
166
166
  status: "SUCCEEDED";
167
167
  moduleRef: "dev-tunnel";
168
- moduleVersion: "0.1.21";
168
+ moduleVersion: "0.1.23";
169
169
  data: SetupState;
170
170
  };
171
171
  observedEffects: string[];
@@ -178,7 +178,7 @@ export declare const behaviorAdapter: {
178
178
  readonly ok: true;
179
179
  readonly status: "SUCCEEDED";
180
180
  readonly moduleRef: "dev-tunnel";
181
- readonly moduleVersion: "0.1.21";
181
+ readonly moduleVersion: "0.1.23";
182
182
  };
183
183
  observedEffects: never[];
184
184
  }>;
@@ -188,14 +188,14 @@ export declare const behaviorAdapter: {
188
188
  readonly ok: true;
189
189
  readonly status: "SUCCEEDED";
190
190
  readonly moduleRef: "dev-tunnel";
191
- readonly moduleVersion: "0.1.21";
191
+ readonly moduleVersion: "0.1.23";
192
192
  };
193
193
  observedEffects: string[];
194
194
  } | {
195
195
  result: {
196
196
  contract: "deployment.result.v1";
197
197
  moduleRef: "dev-tunnel";
198
- moduleVersion: "0.1.21";
198
+ moduleVersion: "0.1.23";
199
199
  ok: false;
200
200
  status: "FAILED";
201
201
  error: {
@@ -212,7 +212,7 @@ export declare const behaviorAdapter: {
212
212
  ok: true;
213
213
  status: "SUCCEEDED";
214
214
  moduleRef: "dev-tunnel";
215
- moduleVersion: "0.1.21";
215
+ moduleVersion: "0.1.23";
216
216
  data: {
217
217
  setupStatus: "ACTION_REQUIRED" | "READY";
218
218
  runtimeStatus: "FAILED" | "RUNNING" | "STOPPED";
@@ -239,67 +239,67 @@ export declare const behaviorAdapter: {
239
239
  ok: true;
240
240
  status: "SUCCEEDED";
241
241
  moduleRef: "dev-tunnel";
242
- moduleVersion: "0.1.21";
242
+ moduleVersion: "0.1.23";
243
243
  data: {
244
244
  docs: string;
245
245
  };
246
246
  };
247
247
  observedEffects: never[];
248
248
  }>;
249
- readonly start: (context: ModuleCommandContext) => Promise<{
249
+ readonly stop: (context: ModuleCommandContext) => Promise<{
250
+ result: {
251
+ readonly contract: "deployment.result.v1";
252
+ readonly ok: true;
253
+ readonly status: "SUCCEEDED";
254
+ readonly moduleRef: "dev-tunnel";
255
+ readonly moduleVersion: "0.1.23";
256
+ };
257
+ observedEffects: string[];
258
+ } | {
250
259
  result: {
251
260
  contract: "deployment.result.v1";
252
261
  moduleRef: "dev-tunnel";
253
- moduleVersion: "0.1.21";
262
+ moduleVersion: "0.1.23";
254
263
  ok: false;
255
264
  status: "FAILED";
256
265
  error: {
257
- code: "START_FAILED";
266
+ code: "STOP_FAILED";
258
267
  message: string;
259
268
  retryable: boolean;
260
269
  };
261
270
  };
262
271
  observedEffects: never[];
263
- } | {
264
- result: {
265
- contract: "deployment.result.v1";
266
- ok: true;
267
- status: "SUCCEEDED";
268
- moduleRef: "dev-tunnel";
269
- moduleVersion: "0.1.21";
270
- data: import("../src/resource-adapter.ts").DevTunnelObservation;
271
- };
272
- observedEffects: string[];
273
272
  }>;
274
- readonly stop: (context: ModuleCommandContext) => Promise<{
275
- result: {
276
- readonly contract: "deployment.result.v1";
277
- readonly ok: true;
278
- readonly status: "SUCCEEDED";
279
- readonly moduleRef: "dev-tunnel";
280
- readonly moduleVersion: "0.1.21";
281
- };
282
- observedEffects: string[];
283
- } | {
273
+ readonly start: (context: ModuleCommandContext) => Promise<{
284
274
  result: {
285
275
  contract: "deployment.result.v1";
286
276
  moduleRef: "dev-tunnel";
287
- moduleVersion: "0.1.21";
277
+ moduleVersion: "0.1.23";
288
278
  ok: false;
289
279
  status: "FAILED";
290
280
  error: {
291
- code: "STOP_FAILED";
281
+ code: "START_FAILED";
292
282
  message: string;
293
283
  retryable: boolean;
294
284
  };
295
285
  };
296
286
  observedEffects: never[];
287
+ } | {
288
+ result: {
289
+ contract: "deployment.result.v1";
290
+ ok: true;
291
+ status: "SUCCEEDED";
292
+ moduleRef: "dev-tunnel";
293
+ moduleVersion: "0.1.23";
294
+ data: import("../src/resource-adapter.ts").DevTunnelObservation;
295
+ };
296
+ observedEffects: string[];
297
297
  }>;
298
298
  readonly setup: (context: ModuleCommandContext) => Promise<{
299
299
  result: {
300
300
  contract: "deployment.result.v1";
301
301
  moduleRef: "dev-tunnel";
302
- moduleVersion: "0.1.21";
302
+ moduleVersion: "0.1.23";
303
303
  ok: false;
304
304
  status: "FAILED";
305
305
  error: {
@@ -315,7 +315,7 @@ export declare const behaviorAdapter: {
315
315
  ok: true;
316
316
  status: "SUCCEEDED";
317
317
  moduleRef: "dev-tunnel";
318
- moduleVersion: "0.1.21";
318
+ moduleVersion: "0.1.23";
319
319
  data: SetupState;
320
320
  };
321
321
  observedEffects: string[];
@@ -3,8 +3,8 @@ import { readFileSync } from "node:fs";
3
3
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
4
4
  import { join, resolve } from "node:path";
5
5
  import { readModuleSharedFacts, writeModuleSharedFacts, } from "@tomflow/proflow-module-contract";
6
- import { createDevTunnelAutomation, createDevTunnelRuntime, verifyProvisionedPublicBaseUrl, } from "../src/resource-adapter.js";
7
6
  import { devTunnelCliPath, resolveDevTunnelCli } from "../src/cli-resolver.js";
7
+ import { createDevTunnelAutomation, createDevTunnelRuntime, verifyProvisionedPublicBaseUrl, } from "../src/resource-adapter.js";
8
8
  import { descriptor } from "./descriptor.js";
9
9
  const base = {
10
10
  contract: "deployment.result.v1",
@@ -367,6 +367,50 @@ export function createDevTunnelBehaviorAdapter(dependencies) {
367
367
  const verifyPublicBaseUrl = dependencies?.verifyPublicBaseUrl ?? verifyProvisionedPublicBaseUrl;
368
368
  return {
369
369
  ...baseBehaviorAdapter,
370
+ start: async (context) => {
371
+ const state = await readState(context);
372
+ if (state?.phase !== "READY" || !state.publicBaseUrl)
373
+ return baseBehaviorAdapter.start(context);
374
+ const rt = createRuntime({
375
+ command: devTunnelCliPath(),
376
+ tunnelId: state.tunnelId,
377
+ publicBaseUrl: state.publicBaseUrl,
378
+ processStateFile: processFile(context),
379
+ });
380
+ try {
381
+ const observed = await rt.start();
382
+ if (observed.login !== "LOGGED_IN")
383
+ throw new Error("Microsoft Dev Tunnel login is not ready");
384
+ if (observed.state !== "RUNNING")
385
+ throw new Error("dev-tunnel did not reach RUNNING");
386
+ try {
387
+ await verifyPublicBaseUrl(state.publicBaseUrl);
388
+ }
389
+ catch (error) {
390
+ await rt.stop().catch(() => undefined);
391
+ throw error;
392
+ }
393
+ return {
394
+ result: { ...base, data: observed },
395
+ observedEffects: [processEffect],
396
+ };
397
+ }
398
+ catch (error) {
399
+ return {
400
+ result: {
401
+ ...base,
402
+ ok: false,
403
+ status: "FAILED",
404
+ error: {
405
+ code: "START_FAILED",
406
+ message: error instanceof Error ? error.message : "failed to start dev-tunnel",
407
+ retryable: true,
408
+ },
409
+ },
410
+ observedEffects: [],
411
+ };
412
+ }
413
+ },
370
414
  setup: async (context) => {
371
415
  try {
372
416
  const previous = await readState(context);
@@ -3,7 +3,7 @@ export declare const descriptor: {
3
3
  readonly contractVersion: "1.0.0";
4
4
  readonly moduleRef: "dev-tunnel";
5
5
  readonly packageName: "@tomflow/proflow-dev-tunnel";
6
- readonly moduleVersion: "0.1.21";
6
+ readonly moduleVersion: "0.1.23";
7
7
  readonly kind: "external-resource";
8
8
  readonly templateVersion: "1.0.0";
9
9
  readonly platformCompatibility: ">=1.0.0 <2.0.0";
@@ -3,7 +3,7 @@ export const descriptor = {
3
3
  contractVersion: "1.0.0",
4
4
  moduleRef: "dev-tunnel",
5
5
  packageName: "@tomflow/proflow-dev-tunnel",
6
- moduleVersion: "0.1.21",
6
+ moduleVersion: "0.1.23",
7
7
  kind: "external-resource",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
@@ -62,7 +62,9 @@ async function version(command) {
62
62
  }
63
63
  }
64
64
  async function sha256(path) {
65
- return createHash("sha256").update(await readFile(path)).digest("hex");
65
+ return createHash("sha256")
66
+ .update(await readFile(path))
67
+ .digest("hex");
66
68
  }
67
69
  async function validCached(path) {
68
70
  try {
@@ -94,6 +94,14 @@ function parseTunnel(input) {
94
94
  return z.object({ tunnel: tunnelPayloadSchema }).passthrough().parse(input)
95
95
  .tunnel;
96
96
  }
97
+ function sameTunnelIdentity(requested, observed) {
98
+ if (observed === requested)
99
+ return true;
100
+ if (requested.includes("."))
101
+ return false;
102
+ const prefix = `${requested}.`;
103
+ return (observed.startsWith(prefix) && !observed.slice(prefix.length).includes("."));
104
+ }
97
105
  function parseJson(text, label) {
98
106
  try {
99
107
  return JSON.parse(text);
@@ -174,7 +182,9 @@ export function createDevTunnelAutomation(input) {
174
182
  const command = input?.command ?? "devtunnel";
175
183
  const run = input?.runCommand ?? defaultCommandRunner;
176
184
  const runRemoteQuery = async (args, retryTimeout = true) => {
177
- let result = await run(command, args, { timeoutMs: REMOTE_QUERY_TIMEOUT_MS });
185
+ let result = await run(command, args, {
186
+ timeoutMs: REMOTE_QUERY_TIMEOUT_MS,
187
+ });
178
188
  if (!retryTimeout ||
179
189
  result.exitCode !== null ||
180
190
  !/timed out/i.test(commandText(result)))
@@ -225,7 +235,7 @@ export function createDevTunnelAutomation(input) {
225
235
  }
226
236
  try {
227
237
  const tunnel = parseTunnel(parseJson(result.stdout, "devtunnel show --json"));
228
- if (tunnel.tunnelId !== tunnelId)
238
+ if (!sameTunnelIdentity(tunnelId, tunnel.tunnelId))
229
239
  return { state: "UNKNOWN", hostState: "UNKNOWN" };
230
240
  const hostState = tunnel.hostConnections !== undefined
231
241
  ? tunnel.hostConnections > 0
@@ -246,9 +256,9 @@ export function createDevTunnelAutomation(input) {
246
256
  const result = await run(command, ["create", tunnelId, "--allow-anonymous", "--json"], { timeoutMs: REMOTE_COMMAND_TIMEOUT_MS });
247
257
  assertCommandSucceeded(result, "devtunnel create --json");
248
258
  const tunnel = parseTunnel(parseJson(result.stdout, "devtunnel create --json"));
249
- if (tunnel.tunnelId !== tunnelId)
259
+ if (!sameTunnelIdentity(tunnelId, tunnel.tunnelId))
250
260
  throw new Error("devtunnel create returned an unexpected tunnelId");
251
- return tunnel.tunnelId;
261
+ return tunnelId;
252
262
  },
253
263
  async ensurePort(tunnelId, port) {
254
264
  const listed = await runRemoteQuery(["port", "list", tunnelId, "--json"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-dev-tunnel",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -3,7 +3,7 @@
3
3
  "contractVersion": "1.0.0",
4
4
  "moduleRef": "dev-tunnel",
5
5
  "packageName": "@tomflow/proflow-dev-tunnel",
6
- "moduleVersion": "0.1.21",
6
+ "moduleVersion": "0.1.23",
7
7
  "kind": "external-resource",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",