ninepm 0.68.0 → 0.69.0
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 +11 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +45 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,6 +62,15 @@ Deploy a static directory:
|
|
|
62
62
|
9pm deploy . --name my-app --slug my-app
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
+
### How long an app's address stays the same
|
|
66
|
+
|
|
67
|
+
Check this before printing a link or QR code. The live URL is `<slug>-<suffix>.9pm.ai`, and the suffix comes from the app's internal id, not from the slug:
|
|
68
|
+
|
|
69
|
+
- Redeploying under the same slug keeps the address.
|
|
70
|
+
- Deleting the app and creating it again under the same slug gives it a **new** address, because the new app gets a new id. Anything printed with the old one stops working.
|
|
71
|
+
- There is no rename: a different address means a different app.
|
|
72
|
+
- A custom domain you own, added on the dashboard's Domains page, gives the app an address of your own. Not every plan includes custom domains.
|
|
73
|
+
|
|
65
74
|
Deploy a built framework output directory:
|
|
66
75
|
|
|
67
76
|
```sh
|
|
@@ -137,6 +146,8 @@ Delete an app, its managed database, managed container app, and retained image:
|
|
|
137
146
|
9pm delete my-app --confirm my-app
|
|
138
147
|
```
|
|
139
148
|
|
|
149
|
+
The app's address goes with it: creating an app again under the same slug gives it a new address, so printed links and QR codes stop working. `9pm delete` prints this warning before it deletes anything.
|
|
150
|
+
|
|
140
151
|
## Access Modes
|
|
141
152
|
|
|
142
153
|
Apps are public by default.
|
package/dist/index.d.ts
CHANGED
|
@@ -124,7 +124,7 @@ export interface CreatedServiceToken {
|
|
|
124
124
|
clientSecret: string;
|
|
125
125
|
}
|
|
126
126
|
export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
127
|
-
export declare const CLI_VERSION = "0.
|
|
127
|
+
export declare const CLI_VERSION = "0.69.0";
|
|
128
128
|
type CredentialBackend = "macos-keychain" | "windows-dpapi" | "linux-secret-service" | "file-plaintext" | "none";
|
|
129
129
|
export interface StoredCredentialEntry {
|
|
130
130
|
token: string;
|
|
@@ -343,6 +343,7 @@ export interface ListSitesResult {
|
|
|
343
343
|
export declare function listSites(options: ApiOptions & {
|
|
344
344
|
owner?: string;
|
|
345
345
|
}): Promise<ListSitesResult>;
|
|
346
|
+
export declare function deleteWarningLines(slug: string): string[];
|
|
346
347
|
export declare function deleteSite(options: DeleteSiteOptions): Promise<DeletedSiteResult>;
|
|
347
348
|
export interface DeviceAuthFeedbackOptions {
|
|
348
349
|
progressStream?: NodeJS.WriteStream;
|
|
@@ -676,6 +677,17 @@ export declare function parseEnvFileEntries(content: string, kind: "secret" | "v
|
|
|
676
677
|
value: string;
|
|
677
678
|
kind: "secret" | "var";
|
|
678
679
|
}>;
|
|
680
|
+
export type ListedDeployment = Pick<Deployment, "id" | "version" | "status" | "createdAt"> & {
|
|
681
|
+
active: boolean;
|
|
682
|
+
};
|
|
683
|
+
export declare function deploymentStatusLabel(deployment: ListedDeployment): string;
|
|
684
|
+
export declare function formatDeploymentsList(deployments: ListedDeployment[]): string[];
|
|
685
|
+
export declare function deploymentsListJson<T extends ListedDeployment>(deployments: T[]): {
|
|
686
|
+
servingDeploymentId: string | null;
|
|
687
|
+
deployments: Array<T & {
|
|
688
|
+
serving: boolean;
|
|
689
|
+
}>;
|
|
690
|
+
};
|
|
679
691
|
export declare function healthWithReason(status: string | null | undefined, reason: string | null | undefined): string;
|
|
680
692
|
export declare function workerPublicFiles(paths: readonly string[], serverEntry: string, selection: string | null): Array<{
|
|
681
693
|
path: string;
|
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ export function deployDatabaseClause(databases) {
|
|
|
49
49
|
const listed = databases.map((database) => (database.name ? `${database.bindingName}:${database.name}` : database.bindingName)).join(", ");
|
|
50
50
|
return `${databases.length === 1 ? "database" : "databases"} ${listed}`;
|
|
51
51
|
}
|
|
52
|
-
export const CLI_VERSION = "0.
|
|
52
|
+
export const CLI_VERSION = "0.69.0";
|
|
53
53
|
const PUBLIC_DASHBOARD_URL = "https://9pm.ai/dashboard";
|
|
54
54
|
const TOKEN_SERVICE = "9pm";
|
|
55
55
|
const TOKEN_ACCOUNT = "default";
|
|
@@ -1380,6 +1380,16 @@ export async function listSites(options) {
|
|
|
1380
1380
|
const payload = await apiRequest(options, route, { method: "GET" });
|
|
1381
1381
|
return { projects: payload.projects };
|
|
1382
1382
|
}
|
|
1383
|
+
// Printed before the delete request, never as a prompt: agents run this non-interactively
|
|
1384
|
+
// and `--confirm` is already the consent. The address part is the one nobody expects:
|
|
1385
|
+
// the hostname's suffix comes from the project id (appHostnameForProject), a new app gets
|
|
1386
|
+
// a new random id, and no route renames a project, so the same slug never gets it back.
|
|
1387
|
+
export function deleteWarningLines(slug) {
|
|
1388
|
+
return [
|
|
1389
|
+
`Warning: deleting ${slug} also gives up its address for good. An app created again under the same slug gets a new address, so links, bookmarks and printed QR codes pointing at the current one will stop working.`,
|
|
1390
|
+
"Its deployments, files and managed database (if it has one) are deleted with it and cannot be recovered.",
|
|
1391
|
+
];
|
|
1392
|
+
}
|
|
1383
1393
|
export async function deleteSite(options) {
|
|
1384
1394
|
const route = `/v1/sites/${encodeURIComponent(options.site)}${options.owner ? `?owner=${encodeURIComponent(options.owner)}` : ""}`;
|
|
1385
1395
|
const payload = await apiRequest(options, route, {
|
|
@@ -3116,21 +3126,45 @@ function parseDeploymentsArgs(argv) {
|
|
|
3116
3126
|
}
|
|
3117
3127
|
throw new Error(`Unknown deployments subcommand: ${subcommand}`);
|
|
3118
3128
|
}
|
|
3129
|
+
export function deploymentStatusLabel(deployment) {
|
|
3130
|
+
if (deployment.active) {
|
|
3131
|
+
return "serving";
|
|
3132
|
+
}
|
|
3133
|
+
return deployment.status === "active" ? "active, not serving" : deployment.status;
|
|
3134
|
+
}
|
|
3135
|
+
export function formatDeploymentsList(deployments) {
|
|
3136
|
+
if (deployments.length === 0) {
|
|
3137
|
+
return ["No deployments."];
|
|
3138
|
+
}
|
|
3139
|
+
const header = ["ID", "VERSION", "STATUS", "CREATED"];
|
|
3140
|
+
const rows = deployments.map((deployment) => [deployment.id, `v${deployment.version}`, deploymentStatusLabel(deployment), deployment.createdAt]);
|
|
3141
|
+
const widths = header.map((title, column) => Math.max(title.length, ...rows.map((row) => (row[column] ?? "").length)));
|
|
3142
|
+
const render = (cells) => cells.map((cell, column) => (column === cells.length - 1 ? cell : cell.padEnd(widths[column] ?? 0))).join(" ");
|
|
3143
|
+
const lines = [render(header), ...rows.map(render), ""];
|
|
3144
|
+
const serving = deployments.find((deployment) => deployment.active);
|
|
3145
|
+
lines.push(serving ? `Serving: v${serving.version} (${serving.id}).` : "No deployment is serving this app.");
|
|
3146
|
+
if (deployments.some((deployment) => !deployment.active && deployment.status === "active")) {
|
|
3147
|
+
lines.push('Rows marked "active, not serving" are earlier deploys that finished; they no longer receive traffic.');
|
|
3148
|
+
}
|
|
3149
|
+
return lines;
|
|
3150
|
+
}
|
|
3151
|
+
export function deploymentsListJson(deployments) {
|
|
3152
|
+
return {
|
|
3153
|
+
servingDeploymentId: deployments.find((deployment) => deployment.active)?.id ?? null,
|
|
3154
|
+
deployments: deployments.map((deployment) => ({ ...deployment, serving: deployment.active })),
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3119
3157
|
async function runDeploymentsCommand(command) {
|
|
3120
3158
|
const base = { apiUrl: command.apiUrl, token: command.token };
|
|
3121
3159
|
const action = command.action;
|
|
3122
3160
|
if (action.kind === "ls") {
|
|
3123
3161
|
const result = await apiRequest(base, `/v1/sites/${encodeURIComponent(action.site)}/deployments`, { method: "GET" });
|
|
3124
3162
|
if (action.json) {
|
|
3125
|
-
console.log(JSON.stringify(result, null, 2));
|
|
3163
|
+
console.log(JSON.stringify({ ...result, ...deploymentsListJson(result.deployments) }, null, 2));
|
|
3126
3164
|
return;
|
|
3127
3165
|
}
|
|
3128
|
-
|
|
3129
|
-
console.log(
|
|
3130
|
-
return;
|
|
3131
|
-
}
|
|
3132
|
-
for (const deployment of result.deployments) {
|
|
3133
|
-
console.log(`${deployment.active ? "*" : " "} ${deployment.id}\tv${deployment.version}\t${deployment.status}\t${deployment.createdAt}`);
|
|
3166
|
+
for (const line of formatDeploymentsList(result.deployments)) {
|
|
3167
|
+
console.log(line);
|
|
3134
3168
|
}
|
|
3135
3169
|
return;
|
|
3136
3170
|
}
|
|
@@ -4700,6 +4734,9 @@ async function main() {
|
|
|
4700
4734
|
return;
|
|
4701
4735
|
}
|
|
4702
4736
|
if (command.kind === "delete") {
|
|
4737
|
+
for (const line of deleteWarningLines(command.confirmSlug)) {
|
|
4738
|
+
console.warn(line);
|
|
4739
|
+
}
|
|
4703
4740
|
const deleted = await deleteSite({
|
|
4704
4741
|
apiUrl: command.apiUrl,
|
|
4705
4742
|
token: command.token,
|