@cardelli/ambit 0.1.4 → 0.1.5
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/esm/deno.js +1 -1
- package/esm/deps/jsr.io/@zod/zod/4.3.6/src/v4/core/json-schema-generator.d.ts +1 -1
- package/esm/lib/cli.d.ts +0 -1
- package/esm/lib/cli.d.ts.map +1 -1
- package/esm/lib/cli.js +41 -26
- package/esm/lib/command.d.ts +23 -48
- package/esm/lib/command.d.ts.map +1 -1
- package/esm/lib/command.js +60 -89
- package/esm/lib/output.d.ts +1 -1
- package/esm/lib/output.d.ts.map +1 -1
- package/esm/lib/output.js +5 -9
- package/esm/lib/result.d.ts +19 -7
- package/esm/lib/result.d.ts.map +1 -1
- package/esm/lib/result.js +47 -1
- package/esm/src/cli/commands/create.js +110 -96
- package/esm/src/cli/commands/deploy.js +17 -13
- package/esm/src/cli/commands/destroy.js +3 -3
- package/esm/src/cli/commands/doctor.js +1 -1
- package/esm/src/discovery.d.ts +4 -1
- package/esm/src/discovery.d.ts.map +1 -1
- package/esm/src/discovery.js +15 -1
- package/esm/src/docker/router/start.sh +18 -72
- package/esm/src/providers/fly.d.ts +6 -0
- package/esm/src/providers/fly.d.ts.map +1 -1
- package/esm/src/providers/fly.js +77 -81
- package/esm/src/providers/tailscale.d.ts.map +1 -1
- package/esm/src/providers/tailscale.js +5 -11
- package/esm/src/template.d.ts +3 -4
- package/esm/src/template.d.ts.map +1 -1
- package/esm/src/template.js +15 -17
- package/package.json +7 -1
|
@@ -5,8 +5,10 @@ set -e
|
|
|
5
5
|
# ambit - Self-Configuring Tailscale Subnet Router
|
|
6
6
|
# =============================================================================
|
|
7
7
|
# State is persisted to /var/lib/tailscale via Fly volume.
|
|
8
|
-
# On first run:
|
|
8
|
+
# On first run: authenticates with a pre-minted auth key, advertises routes.
|
|
9
9
|
# On restart: reuses existing state, no new device created.
|
|
10
|
+
# The router never receives the user's API token — only a single-use,
|
|
11
|
+
# tag-scoped auth key that expires after 5 minutes.
|
|
10
12
|
# =============================================================================
|
|
11
13
|
|
|
12
14
|
echo "Router: Enabling IP Forwarding"
|
|
@@ -33,42 +35,9 @@ if /usr/local/bin/tailscale status --json 2>/dev/null | jq -e '.BackendState ==
|
|
|
33
35
|
--hostname="${FLY_APP_NAME:-ambit}" \
|
|
34
36
|
--advertise-routes="${SUBNET}"
|
|
35
37
|
else
|
|
36
|
-
# First run -
|
|
37
|
-
if [ -
|
|
38
|
-
echo "Router:
|
|
39
|
-
|
|
40
|
-
TAGS_JSON="[]"
|
|
41
|
-
if [ -n "${TAILSCALE_TAGS}" ]; then
|
|
42
|
-
TAGS_JSON=$(echo "${TAILSCALE_TAGS}" | jq -R 'split(",")')
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
AUTH_KEY_RESPONSE=$(curl -s -X POST \
|
|
46
|
-
-u "${TAILSCALE_API_TOKEN}:" \
|
|
47
|
-
-H "Content-Type: application/json" \
|
|
48
|
-
-d "$(jq -n \
|
|
49
|
-
--argjson tags "${TAGS_JSON}" \
|
|
50
|
-
'{
|
|
51
|
-
capabilities: { devices: { create: {
|
|
52
|
-
reusable: false,
|
|
53
|
-
ephemeral: false,
|
|
54
|
-
preauthorized: true,
|
|
55
|
-
tags: $tags
|
|
56
|
-
}}},
|
|
57
|
-
expirySeconds: 300
|
|
58
|
-
}' | jq 'if .capabilities.devices.create.tags == [] then .capabilities.devices.create |= del(.tags) else . end')" \
|
|
59
|
-
"https://api.tailscale.com/api/v2/tailnet/-/keys")
|
|
60
|
-
|
|
61
|
-
TAILSCALE_AUTHKEY=$(echo "${AUTH_KEY_RESPONSE}" | jq -r '.key')
|
|
62
|
-
|
|
63
|
-
if [ -z "${TAILSCALE_AUTHKEY}" ] || [ "${TAILSCALE_AUTHKEY}" = "null" ]; then
|
|
64
|
-
echo "Router: ERROR - Failed to Create Auth Key"
|
|
65
|
-
echo "${AUTH_KEY_RESPONSE}"
|
|
66
|
-
exit 1
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
echo "Router: Auth Key Created"
|
|
70
|
-
elif [ -z "${TAILSCALE_AUTHKEY}" ]; then
|
|
71
|
-
echo "Router: ERROR - No TAILSCALE_API_TOKEN or TAILSCALE_AUTHKEY Provided"
|
|
38
|
+
# First run - authenticate with pre-minted auth key
|
|
39
|
+
if [ -z "${TAILSCALE_AUTHKEY}" ]; then
|
|
40
|
+
echo "Router: ERROR - No TAILSCALE_AUTHKEY Provided"
|
|
72
41
|
exit 1
|
|
73
42
|
fi
|
|
74
43
|
|
|
@@ -79,40 +48,6 @@ else
|
|
|
79
48
|
--advertise-routes="${SUBNET}"
|
|
80
49
|
fi
|
|
81
50
|
|
|
82
|
-
echo "Router: Getting Node Key"
|
|
83
|
-
NODE_KEY=$(/usr/local/bin/tailscale status --json | jq -r '.Self.PublicKey')
|
|
84
|
-
echo "Router: Node Key ${NODE_KEY}"
|
|
85
|
-
|
|
86
|
-
# Self-approve routes if we have API access
|
|
87
|
-
# This is a fallback — if the user has autoApprovers configured in their
|
|
88
|
-
# Tailscale policy file, routes are approved automatically and this block
|
|
89
|
-
# is a no-op (routes are already enabled).
|
|
90
|
-
if [ -n "${TAILSCALE_API_TOKEN}" ]; then
|
|
91
|
-
echo "Router: Finding Device ID"
|
|
92
|
-
|
|
93
|
-
DEVICES_RESPONSE=$(curl -s \
|
|
94
|
-
-u "${TAILSCALE_API_TOKEN}:" \
|
|
95
|
-
"https://api.tailscale.com/api/v2/tailnet/-/devices")
|
|
96
|
-
|
|
97
|
-
DEVICE_ID=$(echo "${DEVICES_RESPONSE}" | jq -r ".devices[] | select(.nodeKey == \"${NODE_KEY}\") | .id")
|
|
98
|
-
|
|
99
|
-
if [ -n "${DEVICE_ID}" ] && [ "${DEVICE_ID}" != "null" ]; then
|
|
100
|
-
echo "Router: Device ID ${DEVICE_ID}"
|
|
101
|
-
echo "Router: Approving Subnet Routes"
|
|
102
|
-
|
|
103
|
-
curl -s -X POST \
|
|
104
|
-
-u "${TAILSCALE_API_TOKEN}:" \
|
|
105
|
-
-H "Content-Type: application/json" \
|
|
106
|
-
-d "{\"routes\": [\"${SUBNET}\"]}" \
|
|
107
|
-
"https://api.tailscale.com/api/v2/device/${DEVICE_ID}/routes" > /dev/null
|
|
108
|
-
|
|
109
|
-
echo "Router: Routes Approved"
|
|
110
|
-
else
|
|
111
|
-
echo "Router: WARNING - Could Not Find Device ID"
|
|
112
|
-
echo "Router: Routes May Need Manual Approval"
|
|
113
|
-
fi
|
|
114
|
-
fi
|
|
115
|
-
|
|
116
51
|
echo "Router: Fully Configured"
|
|
117
52
|
|
|
118
53
|
# Start SOCKS5 proxy for bidirectional tailnet access
|
|
@@ -124,10 +59,21 @@ echo "Router: Starting DNS Proxy"
|
|
|
124
59
|
|
|
125
60
|
# Generate Corefile for CoreDNS
|
|
126
61
|
# Rewrites NETWORK_NAME TLD to .flycast before forwarding to Fly DNS.
|
|
62
|
+
# When ROUTER_ID is set, workload app names are suffixed: app.network ->
|
|
63
|
+
# app-ROUTER_ID.flycast. This ties workloads to their router and avoids
|
|
64
|
+
# name collisions across networks.
|
|
127
65
|
# .flycast resolves to the Flycast address (private_v6) which routes through
|
|
128
66
|
# Fly Proxy — enabling autostart/autostop and load balancing. The Flycast IP
|
|
129
67
|
# is within the network's /48 subnet so it's routable through the tailnet.
|
|
130
|
-
if [ -n "${NETWORK_NAME}" ]; then
|
|
68
|
+
if [ -n "${NETWORK_NAME}" ] && [ -n "${ROUTER_ID}" ]; then
|
|
69
|
+
echo "Router: DNS Rewrite *.${NETWORK_NAME} -> *-${ROUTER_ID}.flycast"
|
|
70
|
+
cat > /etc/coredns/Corefile <<EOF
|
|
71
|
+
.:53 {
|
|
72
|
+
rewrite name regex (.+)\.${NETWORK_NAME}\. {1}-${ROUTER_ID}.flycast. answer auto
|
|
73
|
+
forward . fdaa::3
|
|
74
|
+
}
|
|
75
|
+
EOF
|
|
76
|
+
elif [ -n "${NETWORK_NAME}" ]; then
|
|
131
77
|
echo "Router: DNS Rewrite ${NETWORK_NAME} -> flycast"
|
|
132
78
|
cat > /etc/coredns/Corefile <<EOF
|
|
133
79
|
.:53 {
|
|
@@ -32,6 +32,7 @@ export interface SafeDeployOptions {
|
|
|
32
32
|
image?: string;
|
|
33
33
|
config?: string;
|
|
34
34
|
region?: string;
|
|
35
|
+
routerId?: string;
|
|
35
36
|
}
|
|
36
37
|
export interface FlyProvider {
|
|
37
38
|
ensureInstalled(): Promise<void>;
|
|
@@ -41,6 +42,7 @@ export interface FlyProvider {
|
|
|
41
42
|
listOrgs(): Promise<Record<string, string>>;
|
|
42
43
|
createApp(name: string, org: string, options?: {
|
|
43
44
|
network?: string;
|
|
45
|
+
routerId?: string;
|
|
44
46
|
}): Promise<void>;
|
|
45
47
|
deleteApp(name: string): Promise<void>;
|
|
46
48
|
listApps(org?: string): Promise<FlyApp[]>;
|
|
@@ -67,4 +69,8 @@ export interface FlyProvider {
|
|
|
67
69
|
}
|
|
68
70
|
export declare const createFlyProvider: () => FlyProvider;
|
|
69
71
|
export declare const getRouterAppName: (network: string, randomSuffix: string) => string;
|
|
72
|
+
/** Extract the routerId suffix from a router app name. */
|
|
73
|
+
export declare const getRouterSuffix: (routerAppName: string, network: string) => string;
|
|
74
|
+
/** Build the physical Fly app name for a workload. */
|
|
75
|
+
export declare const getWorkloadAppName: (name: string, routerId: string) => string;
|
|
70
76
|
//# sourceMappingURL=fly.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fly.d.ts","sourceRoot":"","sources":["../../../src/src/providers/fly.ts"],"names":[],"mappings":"AAGA,OAAO,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"fly.d.ts","sourceRoot":"","sources":["../../../src/src/providers/fly.ts"],"names":[],"mappings":"AAGA,OAAO,yBAAyB,CAAC;AASjC,OAAO,EACL,KAAK,MAAM,EACX,KAAK,UAAU,EAIf,KAAK,KAAK,EAEV,KAAK,UAAU,EAMhB,MAAM,mBAAmB,CAAC;AAa3B;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAMxC;AAmBD,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,CAAC;AAE9E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,aAAa,GACxB,MAAM,WAAW,KAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CASlC,CAAC;AAMF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD,MAAM,WAAW,WAAW;IAC1B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,UAAU,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjE,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5C,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAChD,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACjD,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1D,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1E,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,UAAU,CACR,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,YAAY,CACV,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACvC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAChE,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1C,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;CACzD;AAMD,eAAO,MAAM,iBAAiB,QAAO,WAqbpC,CAAC;AAMF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,MAAM,EACf,cAAc,MAAM,KACnB,MAEF,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,eAAe,GAC1B,eAAe,MAAM,EACrB,SAAS,MAAM,KACd,MAGF,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,UAAU,MAAM,KACf,MAEF,CAAC"}
|
package/esm/src/providers/fly.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// =============================================================================
|
|
4
4
|
import "../../_dnt.polyfills.js";
|
|
5
5
|
import * as dntShim from "../../_dnt.shims.js";
|
|
6
|
-
import { runCommand,
|
|
6
|
+
import { runCommand, runJson, runQuiet } from "../../lib/command.js";
|
|
7
|
+
import { Result } from "../../lib/result.js";
|
|
7
8
|
import { commandExists, die, Spinner } from "../../lib/cli.js";
|
|
8
9
|
import { dirname, resolve } from "../../deps/jsr.io/@std/path/1.1.4/mod.js";
|
|
9
10
|
import { FlyAppInfoListSchema, FlyAppsListSchema, FlyAuthSchema, FlyIpListSchema, FlyMachinesListSchema, FlyOrgsSchema, FlyStatusSchema, mapFlyMachineSize, mapFlyMachineState, } from "../schemas/fly.js";
|
|
@@ -64,40 +65,40 @@ export const createFlyProvider = () => {
|
|
|
64
65
|
async ensureAuth(options) {
|
|
65
66
|
const interactive = options?.interactive ?? true;
|
|
66
67
|
const result = await runCommand(["fly", "auth", "whoami", "--json"]);
|
|
67
|
-
if (result.
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
if (result.ok) {
|
|
69
|
+
const auth = result.json();
|
|
70
|
+
if (auth.ok) {
|
|
71
|
+
const parsed = FlyAuthSchema.safeParse(auth.value);
|
|
70
72
|
if (parsed.success) {
|
|
71
73
|
return parsed.data.email;
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
|
-
catch {
|
|
75
|
-
// Parse failed, need to authenticate
|
|
76
|
-
}
|
|
77
76
|
}
|
|
78
77
|
if (!interactive) {
|
|
79
78
|
return die("Not Authenticated with Fly.io. Run 'fly auth login' First");
|
|
80
79
|
}
|
|
81
|
-
const loginResult = await
|
|
82
|
-
|
|
80
|
+
const loginResult = await runCommand(["fly", "auth", "login"], {
|
|
81
|
+
interactive: true,
|
|
82
|
+
});
|
|
83
|
+
if (!loginResult.ok) {
|
|
83
84
|
return die("Fly.io Authentication Failed");
|
|
84
85
|
}
|
|
85
86
|
const checkResult = await runCommand(["fly", "auth", "whoami", "--json"]);
|
|
86
|
-
if (!checkResult.
|
|
87
|
+
if (!checkResult.ok) {
|
|
87
88
|
return die("Fly.io Authentication Verification Failed");
|
|
88
89
|
}
|
|
89
|
-
const parsed = FlyAuthSchema.safeParse(
|
|
90
|
+
const parsed = FlyAuthSchema.safeParse(checkResult.json().unwrap());
|
|
90
91
|
if (!parsed.success || !parsed.data) {
|
|
91
92
|
return die("Fly.io Authentication Response Invalid");
|
|
92
93
|
}
|
|
93
94
|
return parsed.data.email;
|
|
94
95
|
},
|
|
95
96
|
async listOrgs() {
|
|
96
|
-
const result = await
|
|
97
|
-
if (!result.
|
|
97
|
+
const result = await runJson(["fly", "orgs", "list", "--json"]);
|
|
98
|
+
if (!result.ok) {
|
|
98
99
|
return die("Failed to List Organizations");
|
|
99
100
|
}
|
|
100
|
-
const parsed = FlyOrgsSchema.safeParse(result.
|
|
101
|
+
const parsed = FlyOrgsSchema.safeParse(result.value);
|
|
101
102
|
if (!parsed.success) {
|
|
102
103
|
return die("Failed to Parse Organizations");
|
|
103
104
|
}
|
|
@@ -109,25 +110,24 @@ export const createFlyProvider = () => {
|
|
|
109
110
|
args.push("--org", org);
|
|
110
111
|
}
|
|
111
112
|
const result = await runCommand(args);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
return [];
|
|
121
|
-
}
|
|
113
|
+
return result.json().flatMap((data) => {
|
|
114
|
+
const parsed = FlyAppsListSchema.safeParse(data);
|
|
115
|
+
return parsed.success
|
|
116
|
+
? Result.ok(parsed.data)
|
|
117
|
+
: Result.err("Parse Failed");
|
|
118
|
+
}).unwrapOr([]);
|
|
122
119
|
},
|
|
123
120
|
async createApp(name, org, options) {
|
|
124
|
-
const
|
|
121
|
+
const appName = options?.routerId
|
|
122
|
+
? getWorkloadAppName(name, options.routerId)
|
|
123
|
+
: name;
|
|
124
|
+
const args = ["fly", "apps", "create", appName, "--org", org, "--json"];
|
|
125
125
|
if (options?.network) {
|
|
126
126
|
args.push("--network", options.network);
|
|
127
127
|
}
|
|
128
128
|
const result = await runQuiet("Creating App", args);
|
|
129
|
-
if (!result.
|
|
130
|
-
return die(`Failed to Create App '${
|
|
129
|
+
if (!result.ok) {
|
|
130
|
+
return die(`Failed to Create App '${appName}'`);
|
|
131
131
|
}
|
|
132
132
|
},
|
|
133
133
|
async deleteApp(name) {
|
|
@@ -138,29 +138,28 @@ export const createFlyProvider = () => {
|
|
|
138
138
|
name,
|
|
139
139
|
"--yes",
|
|
140
140
|
]);
|
|
141
|
-
if (!result.
|
|
141
|
+
if (!result.ok) {
|
|
142
142
|
return die(`Failed to Delete App '${name}'`);
|
|
143
143
|
}
|
|
144
144
|
},
|
|
145
145
|
async appExists(name) {
|
|
146
146
|
const result = await runCommand(["fly", "status", "-a", name, "--json"]);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
147
|
+
return result.json().match({
|
|
148
|
+
ok: (data) => {
|
|
149
|
+
const parsed = FlyStatusSchema.safeParse(data);
|
|
150
|
+
return parsed.success && !!parsed.data.ID;
|
|
151
|
+
},
|
|
152
|
+
err: () => false,
|
|
153
|
+
});
|
|
156
154
|
},
|
|
157
155
|
async listMachines(app) {
|
|
158
|
-
const result = await
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
156
|
+
const result = await runJson(["fly", "machines", "list", "-a", app, "--json"]);
|
|
157
|
+
return result.flatMap((data) => {
|
|
158
|
+
const parsed = FlyMachinesListSchema.safeParse(data);
|
|
159
|
+
return parsed.success
|
|
160
|
+
? Result.ok(parsed.data)
|
|
161
|
+
: Result.err("Parse Failed");
|
|
162
|
+
}).unwrapOr([]);
|
|
164
163
|
},
|
|
165
164
|
async listMachinesMapped(app) {
|
|
166
165
|
const raw = await this.listMachines(app);
|
|
@@ -198,7 +197,7 @@ export const createFlyProvider = () => {
|
|
|
198
197
|
const spinner = new Spinner();
|
|
199
198
|
spinner.start(`Creating ${config.size} Machine`);
|
|
200
199
|
const result = await runCommand(args);
|
|
201
|
-
if (!result.
|
|
200
|
+
if (!result.ok) {
|
|
202
201
|
spinner.fail("Machine Creation Failed");
|
|
203
202
|
return die(result.stderr || "Unknown Error");
|
|
204
203
|
}
|
|
@@ -221,7 +220,7 @@ export const createFlyProvider = () => {
|
|
|
221
220
|
app,
|
|
222
221
|
"--force",
|
|
223
222
|
]);
|
|
224
|
-
if (!result.
|
|
223
|
+
if (!result.ok) {
|
|
225
224
|
return die(`Failed to Destroy Machine '${shortId}'`);
|
|
226
225
|
}
|
|
227
226
|
},
|
|
@@ -236,7 +235,7 @@ export const createFlyProvider = () => {
|
|
|
236
235
|
args.push("--stage");
|
|
237
236
|
}
|
|
238
237
|
const result = await runQuiet(`Setting ${pairs.length} Secret(s)`, args);
|
|
239
|
-
if (!result.
|
|
238
|
+
if (!result.ok) {
|
|
240
239
|
return die("Failed to Set Secrets");
|
|
241
240
|
}
|
|
242
241
|
},
|
|
@@ -254,7 +253,7 @@ export const createFlyProvider = () => {
|
|
|
254
253
|
args.push("--primary-region", config.region);
|
|
255
254
|
}
|
|
256
255
|
const result = await runCommand(args);
|
|
257
|
-
if (!result.
|
|
256
|
+
if (!result.ok) {
|
|
258
257
|
throw new FlyDeployError(app, result.stderr);
|
|
259
258
|
}
|
|
260
259
|
},
|
|
@@ -267,15 +266,12 @@ export const createFlyProvider = () => {
|
|
|
267
266
|
app,
|
|
268
267
|
"--json",
|
|
269
268
|
]);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
catch {
|
|
277
|
-
return [];
|
|
278
|
-
}
|
|
269
|
+
return result.json().flatMap((data) => {
|
|
270
|
+
const parsed = FlyIpListSchema.safeParse(data);
|
|
271
|
+
return parsed.success
|
|
272
|
+
? Result.ok(parsed.data)
|
|
273
|
+
: Result.err("Parse Failed");
|
|
274
|
+
}).unwrapOr([]);
|
|
279
275
|
},
|
|
280
276
|
async releaseIp(app, address) {
|
|
281
277
|
const result = await runCommand([
|
|
@@ -286,7 +282,7 @@ export const createFlyProvider = () => {
|
|
|
286
282
|
"-a",
|
|
287
283
|
app,
|
|
288
284
|
]);
|
|
289
|
-
if (!result.
|
|
285
|
+
if (!result.ok) {
|
|
290
286
|
return die(`Failed to Release IP ${address} from '${app}'`);
|
|
291
287
|
}
|
|
292
288
|
},
|
|
@@ -301,22 +297,21 @@ export const createFlyProvider = () => {
|
|
|
301
297
|
"-a",
|
|
302
298
|
app,
|
|
303
299
|
]);
|
|
304
|
-
if (!result.
|
|
300
|
+
if (!result.ok) {
|
|
305
301
|
return die(`Failed to Allocate Flycast IP on Network '${network}'`);
|
|
306
302
|
}
|
|
307
303
|
},
|
|
308
304
|
async getConfig(app) {
|
|
309
305
|
const result = await runCommand(["fly", "config", "show", "-a", app]);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
catch {
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
306
|
+
return result.json().match({
|
|
307
|
+
ok: (v) => v,
|
|
308
|
+
err: () => null,
|
|
309
|
+
});
|
|
318
310
|
},
|
|
319
311
|
async deploySafe(app, options) {
|
|
312
|
+
const appName = options.routerId
|
|
313
|
+
? getWorkloadAppName(app, options.routerId)
|
|
314
|
+
: app;
|
|
320
315
|
const args = ["fly", "deploy"];
|
|
321
316
|
// When config is provided, use its parent directory as the build context
|
|
322
317
|
// so fly deploy finds the Dockerfile and COPY picks up the correct files
|
|
@@ -325,7 +320,7 @@ export const createFlyProvider = () => {
|
|
|
325
320
|
args.push(dirname(configAbs));
|
|
326
321
|
args.push("--config", configAbs);
|
|
327
322
|
}
|
|
328
|
-
args.push("-a",
|
|
323
|
+
args.push("-a", appName, "--yes", "--no-public-ips");
|
|
329
324
|
if (options.image) {
|
|
330
325
|
args.push("--image", options.image);
|
|
331
326
|
}
|
|
@@ -333,8 +328,8 @@ export const createFlyProvider = () => {
|
|
|
333
328
|
args.push("--primary-region", options.region);
|
|
334
329
|
}
|
|
335
330
|
const result = await runCommand(args);
|
|
336
|
-
if (!result.
|
|
337
|
-
throw new FlyDeployError(
|
|
331
|
+
if (!result.ok) {
|
|
332
|
+
throw new FlyDeployError(appName, result.stderr);
|
|
338
333
|
}
|
|
339
334
|
},
|
|
340
335
|
async listCerts(app) {
|
|
@@ -346,17 +341,9 @@ export const createFlyProvider = () => {
|
|
|
346
341
|
app,
|
|
347
342
|
"--json",
|
|
348
343
|
]);
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const certs = JSON.parse(result.stdout);
|
|
353
|
-
return certs
|
|
354
|
-
.map((c) => c.Hostname)
|
|
355
|
-
.filter((h) => typeof h === "string");
|
|
356
|
-
}
|
|
357
|
-
catch {
|
|
358
|
-
return [];
|
|
359
|
-
}
|
|
344
|
+
return result.json().map((certs) => certs
|
|
345
|
+
.map((c) => c.Hostname)
|
|
346
|
+
.filter((h) => typeof h === "string")).unwrapOr([]);
|
|
360
347
|
},
|
|
361
348
|
async removeCert(app, hostname) {
|
|
362
349
|
await runCommand([
|
|
@@ -404,8 +391,17 @@ export const createFlyProvider = () => {
|
|
|
404
391
|
};
|
|
405
392
|
};
|
|
406
393
|
// =============================================================================
|
|
407
|
-
//
|
|
394
|
+
// App Naming
|
|
408
395
|
// =============================================================================
|
|
409
396
|
export const getRouterAppName = (network, randomSuffix) => {
|
|
410
397
|
return `${ROUTER_APP_PREFIX}${network}-${randomSuffix}`;
|
|
411
398
|
};
|
|
399
|
+
/** Extract the routerId suffix from a router app name. */
|
|
400
|
+
export const getRouterSuffix = (routerAppName, network) => {
|
|
401
|
+
const prefix = `${ROUTER_APP_PREFIX}${network}-`;
|
|
402
|
+
return routerAppName.slice(prefix.length);
|
|
403
|
+
};
|
|
404
|
+
/** Build the physical Fly app name for a workload. */
|
|
405
|
+
export const getWorkloadAppName = (name, routerId) => {
|
|
406
|
+
return `${name}-${routerId}`;
|
|
407
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tailscale.d.ts","sourceRoot":"","sources":["../../../src/src/providers/tailscale.ts"],"names":[],"mappings":"AAGA,OAAO,yBAAyB,CAAC;AAKjC,OAAO,EACL,KAAK,mBAAmB,EAExB,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAYjC,MAAM,WAAW,iBAAiB;IAChC,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,aAAa,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC1C,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IACvE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACzD,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzD;AAiBD,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,EACf,QAAQ,MAAM,KACb,iBAoMF,CAAC;AAMF,eAAO,MAAM,aAAa,GACxB,UAAU,iBAAiB,EAC3B,UAAU,MAAM,EAChB,YAAW,MAAe,KACzB,OAAO,CAAC,eAAe,CAazB,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAa,OAAO,CAAC,OAAO,CAE5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"tailscale.d.ts","sourceRoot":"","sources":["../../../src/src/providers/tailscale.ts"],"names":[],"mappings":"AAGA,OAAO,yBAAyB,CAAC;AAKjC,OAAO,EACL,KAAK,mBAAmB,EAExB,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAYjC,MAAM,WAAW,iBAAiB;IAChC,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,aAAa,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC1C,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IACvE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACzD,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzD;AAiBD,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,EACf,QAAQ,MAAM,KACb,iBAoMF,CAAC;AAMF,eAAO,MAAM,aAAa,GACxB,UAAU,iBAAiB,EAC3B,UAAU,MAAM,EAChB,YAAW,MAAe,KACzB,OAAO,CAAC,eAAe,CAazB,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAa,OAAO,CAAC,OAAO,CAE5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,OAAO,CAM7D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,OAAO,CAG1D,CAAC"}
|
|
@@ -174,16 +174,10 @@ export const isTailscaleInstalled = async () => {
|
|
|
174
174
|
*/
|
|
175
175
|
export const isAcceptRoutesEnabled = async () => {
|
|
176
176
|
const result = await runCommand(["tailscale", "debug", "prefs"]);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const prefs = JSON.parse(result.stdout);
|
|
182
|
-
return prefs.RouteAll === true;
|
|
183
|
-
}
|
|
184
|
-
catch {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
177
|
+
return result.json().match({
|
|
178
|
+
ok: (prefs) => prefs.RouteAll === true,
|
|
179
|
+
err: () => false,
|
|
180
|
+
});
|
|
187
181
|
};
|
|
188
182
|
/**
|
|
189
183
|
* Enable accept-routes on the local client.
|
|
@@ -191,5 +185,5 @@ export const isAcceptRoutesEnabled = async () => {
|
|
|
191
185
|
*/
|
|
192
186
|
export const enableAcceptRoutes = async () => {
|
|
193
187
|
const result = await runCommand(["tailscale", "set", "--accept-routes"]);
|
|
194
|
-
return result.
|
|
188
|
+
return result.ok;
|
|
195
189
|
};
|
package/esm/src/template.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export type TemplateErrorKind = "NotFound" | "RateLimited" | "HttpError" | "ExtractionFailed" | "PathNotFound" | "PathNotDirectory" | "EmptyArchive" | "NetworkError";
|
|
1
|
+
import "../_dnt.polyfills.js";
|
|
2
|
+
import { Result } from "../lib/result.js";
|
|
4
3
|
/** Parsed GitHub template reference. */
|
|
5
4
|
export interface TemplateRef {
|
|
6
5
|
owner: string;
|
|
@@ -12,7 +11,7 @@ export interface TemplateRef {
|
|
|
12
11
|
export type TemplateFetchResult = Result<{
|
|
13
12
|
tempDir: string;
|
|
14
13
|
templateDir: string;
|
|
15
|
-
}
|
|
14
|
+
}>;
|
|
16
15
|
/**
|
|
17
16
|
* Parse a template reference string into its components.
|
|
18
17
|
* Returns null if the format is invalid.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/src/template.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/src/template.ts"],"names":[],"mappings":"AAgBA,OAAO,sBAAsB,CAAC;AAO9B,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAM1C,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAED,iDAAiD;AACjD,MAAM,MAAM,mBAAmB,GAAG,MAAM,CACtC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CACzC,CAAC;AAyBF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,WAAW,GAAG,IAyB5D,CAAC;AAMF;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GACxB,KAAK,WAAW,KACf,OAAO,CAAC,mBAAmB,CAqG7B,CAAC"}
|
package/esm/src/template.js
CHANGED
|
@@ -14,18 +14,16 @@
|
|
|
14
14
|
// ToxicPine/ambit-templates/cdp@main → explicit branch
|
|
15
15
|
//
|
|
16
16
|
// =============================================================================
|
|
17
|
+
import "../_dnt.polyfills.js";
|
|
17
18
|
import * as dntShim from "../_dnt.shims.js";
|
|
18
19
|
import { join } from "../deps/jsr.io/@std/path/1.1.4/mod.js";
|
|
19
20
|
import { runCommand } from "../lib/command.js";
|
|
21
|
+
import { Result } from "../lib/result.js";
|
|
20
22
|
// =============================================================================
|
|
21
23
|
// Internal Helpers
|
|
22
24
|
// =============================================================================
|
|
23
25
|
/** Shorthand for returning a typed fetch error. */
|
|
24
|
-
const fail = (
|
|
25
|
-
ok: false,
|
|
26
|
-
kind,
|
|
27
|
-
message,
|
|
28
|
-
});
|
|
26
|
+
const fail = (message) => Result.err(message);
|
|
29
27
|
/** Format a template reference for display. */
|
|
30
28
|
const formatRef = (ref) => {
|
|
31
29
|
const base = ref.path === "."
|
|
@@ -96,9 +94,9 @@ export const fetchTemplate = async (ref) => {
|
|
|
96
94
|
}
|
|
97
95
|
catch { /* ignore */ }
|
|
98
96
|
};
|
|
99
|
-
const cleanFail = (
|
|
97
|
+
const cleanFail = (message) => {
|
|
100
98
|
cleanup();
|
|
101
|
-
return fail(
|
|
99
|
+
return fail(message);
|
|
102
100
|
};
|
|
103
101
|
try {
|
|
104
102
|
const url = ref.ref
|
|
@@ -113,13 +111,13 @@ export const fetchTemplate = async (ref) => {
|
|
|
113
111
|
if (!response.ok) {
|
|
114
112
|
const repo = formatRepo(ref);
|
|
115
113
|
if (response.status === 404) {
|
|
116
|
-
return cleanFail(
|
|
114
|
+
return cleanFail(`Template Repository Not Found: ${repo}. ` +
|
|
117
115
|
"Check that the repository exists and is public.");
|
|
118
116
|
}
|
|
119
117
|
if (response.status === 403) {
|
|
120
|
-
return cleanFail("
|
|
118
|
+
return cleanFail("GitHub API Rate Limit Exceeded. Try again later.");
|
|
121
119
|
}
|
|
122
|
-
return cleanFail(
|
|
120
|
+
return cleanFail(`GitHub API Returned HTTP ${response.status} for ${repo}`);
|
|
123
121
|
}
|
|
124
122
|
// Write tarball to disk
|
|
125
123
|
const tarballPath = join(tempDir, "template.tar.gz");
|
|
@@ -135,31 +133,31 @@ export const fetchTemplate = async (ref) => {
|
|
|
135
133
|
"-C",
|
|
136
134
|
extractDir,
|
|
137
135
|
]);
|
|
138
|
-
if (!extractResult.
|
|
139
|
-
return cleanFail("
|
|
136
|
+
if (!extractResult.ok) {
|
|
137
|
+
return cleanFail("Failed to Extract Template Archive");
|
|
140
138
|
}
|
|
141
139
|
// GitHub tarballs have a single top-level dir (owner-repo-commitish/)
|
|
142
140
|
const entries = [...dntShim.Deno.readDirSync(extractDir)];
|
|
143
141
|
const topLevel = entries.find((e) => e.isDirectory);
|
|
144
142
|
if (!topLevel) {
|
|
145
|
-
return cleanFail("
|
|
143
|
+
return cleanFail("Template Archive Has No Top-Level Directory");
|
|
146
144
|
}
|
|
147
145
|
// Locate the template subdirectory
|
|
148
146
|
const templateDir = join(extractDir, topLevel.name, ref.path);
|
|
149
147
|
try {
|
|
150
148
|
const stat = dntShim.Deno.statSync(templateDir);
|
|
151
149
|
if (!stat.isDirectory) {
|
|
152
|
-
return cleanFail(
|
|
150
|
+
return cleanFail(`Template Path '${ref.path}' Is Not a Directory in ${formatRepo(ref)}`);
|
|
153
151
|
}
|
|
154
152
|
}
|
|
155
153
|
catch {
|
|
156
|
-
return cleanFail(
|
|
154
|
+
return cleanFail(`Template Path '${ref.path}' Not Found in ${formatRepo(ref)}`);
|
|
157
155
|
}
|
|
158
|
-
return {
|
|
156
|
+
return Result.ok({ tempDir, templateDir });
|
|
159
157
|
}
|
|
160
158
|
catch (e) {
|
|
161
159
|
if (e instanceof TypeError) {
|
|
162
|
-
return cleanFail("
|
|
160
|
+
return cleanFail("Network Error: Could Not Reach GitHub");
|
|
163
161
|
}
|
|
164
162
|
cleanup();
|
|
165
163
|
throw e;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardelli/ambit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Deploy apps to the cloud that only you and your AI agents can reach",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "./esm/src/providers/fly.js",
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
"./lib/output": {
|
|
30
30
|
"import": "./esm/lib/output.js"
|
|
31
31
|
},
|
|
32
|
+
"./lib/result": {
|
|
33
|
+
"import": "./esm/lib/result.js"
|
|
34
|
+
},
|
|
32
35
|
"./lib/paths": {
|
|
33
36
|
"import": "./esm/lib/paths.js"
|
|
34
37
|
},
|
|
@@ -43,6 +46,9 @@
|
|
|
43
46
|
},
|
|
44
47
|
"./src/resolve": {
|
|
45
48
|
"import": "./esm/src/resolve.js"
|
|
49
|
+
},
|
|
50
|
+
"./src/template": {
|
|
51
|
+
"import": "./esm/src/template.js"
|
|
46
52
|
}
|
|
47
53
|
},
|
|
48
54
|
"scripts": {},
|