@abyssinia-labs/sokie 0.7.0-staging.138 → 0.8.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/sokie.mjs +332 -159
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -90,6 +90,17 @@ sokie requests # what waits for your answer, and what you ask
|
|
|
90
90
|
sokie requests approve kd7a3f9c # or decline, with --note
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
An owner or admin can also make a server's production deploys wait for one
|
|
94
|
+
of them. A deploy that must wait is held: `sokie deploy` stops there and
|
|
95
|
+
says why, and it serves only the team, signed in, at its own address for up
|
|
96
|
+
to 7 days. It's listed in `sokie requests` for an owner or admin to approve,
|
|
97
|
+
which puts it live:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
sokie call getInvoice --held 12 # try held deploy 12 before anyone approves it
|
|
101
|
+
sokie deploys # held shows as waiting for an admin
|
|
102
|
+
```
|
|
103
|
+
|
|
93
104
|
## Links
|
|
94
105
|
|
|
95
106
|
- [Docs](https://sokie.sh/docs)
|
package/dist/sokie.mjs
CHANGED
|
@@ -24033,9 +24033,9 @@ var require___dev__ = __commonJS((exports, module) => {
|
|
|
24033
24033
|
|
|
24034
24034
|
// package.json
|
|
24035
24035
|
var name = "@abyssinia-labs/sokie";
|
|
24036
|
-
var version = "0.
|
|
24036
|
+
var version = "0.8.0";
|
|
24037
24037
|
var sokie = {
|
|
24038
|
-
site: "https://
|
|
24038
|
+
site: "https://sokie.sh"
|
|
24039
24039
|
};
|
|
24040
24040
|
|
|
24041
24041
|
// src/command.ts
|
|
@@ -44961,6 +44961,7 @@ function utf8Bytes(text3) {
|
|
|
44961
44961
|
// src/deploy.ts
|
|
44962
44962
|
var POLL_MS = 2000;
|
|
44963
44963
|
var WAIT_MS = 5 * 60 * 1000;
|
|
44964
|
+
var DONE = new Set(["live", "failed", "held"]);
|
|
44964
44965
|
|
|
44965
44966
|
class DeployError extends Error {
|
|
44966
44967
|
code;
|
|
@@ -45023,7 +45024,7 @@ async function followDeploy(platform, deploymentId, options) {
|
|
|
45023
45024
|
last = found.status;
|
|
45024
45025
|
await options.progress?.(found.status);
|
|
45025
45026
|
}
|
|
45026
|
-
if (!options.wait ||
|
|
45027
|
+
if (!options.wait || DONE.has(found.status))
|
|
45027
45028
|
return found;
|
|
45028
45029
|
if (Date.now() > deadline) {
|
|
45029
45030
|
throw new DeployError("deploy_timeout", "The deploy is still running; check it in the app.");
|
|
@@ -47368,12 +47369,26 @@ function routeCall(definition, name2, input2) {
|
|
|
47368
47369
|
}
|
|
47369
47370
|
|
|
47370
47371
|
// src/call-target.ts
|
|
47371
|
-
function targetFrom(preview, production) {
|
|
47372
|
-
|
|
47373
|
-
|
|
47372
|
+
function targetFrom({ preview, production = false, held }) {
|
|
47373
|
+
const given = [
|
|
47374
|
+
preview === undefined ? "" : "--preview <n>",
|
|
47375
|
+
held === undefined ? "" : "--held <n>",
|
|
47376
|
+
production ? "--production" : ""
|
|
47377
|
+
].filter(Boolean);
|
|
47378
|
+
if (given.length > 1) {
|
|
47379
|
+
const last = given.pop();
|
|
47380
|
+
const all = given.length === 1 ? "both" : "all three";
|
|
47381
|
+
throw new UsageError(`Say ${given.join(", ")} or ${last}, not ${all}.`);
|
|
47374
47382
|
}
|
|
47375
47383
|
if (production)
|
|
47376
47384
|
return { kind: "production" };
|
|
47385
|
+
if (held !== undefined) {
|
|
47386
|
+
const number5 = Number(held.replace(/^#/, ""));
|
|
47387
|
+
if (!Number.isInteger(number5) || number5 < 1) {
|
|
47388
|
+
throw new UsageError(`--held takes a deploy's number, like 12, not ${held}.`);
|
|
47389
|
+
}
|
|
47390
|
+
return { kind: "held", number: number5 };
|
|
47391
|
+
}
|
|
47377
47392
|
if (preview === undefined)
|
|
47378
47393
|
return { kind: "dev" };
|
|
47379
47394
|
const number4 = Number(preview);
|
|
@@ -47385,6 +47400,8 @@ function targetFrom(preview, production) {
|
|
|
47385
47400
|
function targetName(target) {
|
|
47386
47401
|
if (target.kind === "dev")
|
|
47387
47402
|
return "sokie dev";
|
|
47403
|
+
if (target.kind === "held")
|
|
47404
|
+
return `held deploy #${target.number}`;
|
|
47388
47405
|
return target.kind === "production" ? "production" : `preview #${target.number}`;
|
|
47389
47406
|
}
|
|
47390
47407
|
function refusal2(code2, server, target) {
|
|
@@ -47395,6 +47412,8 @@ function refusal2(code2, server, target) {
|
|
|
47395
47412
|
return new DeployError("not_live", `${server.slug} has nothing live yet, so there's nothing to call. sokie deploy puts it live.`);
|
|
47396
47413
|
case "NO_PREVIEW":
|
|
47397
47414
|
return new DeployError("no_preview", `${server.slug} has no live preview for #${target.kind === "preview" ? target.number : "?"}.`);
|
|
47415
|
+
case "NOT_HELD":
|
|
47416
|
+
return new DeployError("not_held", `Deploy #${target.kind === "held" ? target.number : "?"} of ${server.slug} isn't held now: it went live, or was declined, withdrawn, replaced or lapsed. sokie deploys says which.`);
|
|
47398
47417
|
case "PAUSED":
|
|
47399
47418
|
return new DeployError("paused", `${server.slug} is paused, so it takes no calls.`);
|
|
47400
47419
|
case "RATE_LIMITED":
|
|
@@ -47411,7 +47430,8 @@ async function mintEndpoint(client, server, target) {
|
|
|
47411
47430
|
try {
|
|
47412
47431
|
const minted = await client.action(fn.mintTestToken, {
|
|
47413
47432
|
serverId: server.serverId,
|
|
47414
|
-
...target.kind === "preview" ? { preview: target.number } : {}
|
|
47433
|
+
...target.kind === "preview" ? { preview: target.number } : {},
|
|
47434
|
+
...target.kind === "held" ? { held: target.number } : {}
|
|
47415
47435
|
});
|
|
47416
47436
|
return { url: minted.url, token: minted.token, expiresAt: minted.expiresAt };
|
|
47417
47437
|
} catch (error62) {
|
|
@@ -64638,6 +64658,7 @@ async function runCall(argv, out, deps) {
|
|
|
64638
64658
|
dir: { type: "string" },
|
|
64639
64659
|
server: { type: "string" },
|
|
64640
64660
|
preview: { type: "string" },
|
|
64661
|
+
held: { type: "string" },
|
|
64641
64662
|
production: { type: "boolean", default: false }
|
|
64642
64663
|
});
|
|
64643
64664
|
const [name2, ...extra] = positionals;
|
|
@@ -64645,9 +64666,9 @@ async function runCall(argv, out, deps) {
|
|
|
64645
64666
|
throw new UsageError(`Give one tool's name: sokie call getTask --input '{"taskId":"t_1"}'.`);
|
|
64646
64667
|
}
|
|
64647
64668
|
const input2 = parseInput(values.input);
|
|
64648
|
-
const target = targetFrom(values
|
|
64669
|
+
const target = targetFrom(values);
|
|
64649
64670
|
if (target.kind === "dev" && values.server !== undefined) {
|
|
64650
|
-
throw new UsageError("--server picks a deploy: add --preview <n> or --production.");
|
|
64671
|
+
throw new UsageError("--server picks a deploy: add --preview <n>, --held <n> or --production.");
|
|
64651
64672
|
}
|
|
64652
64673
|
const root = resolve(values.dir ?? ".");
|
|
64653
64674
|
const local = localRoute(root, name2, input2);
|
|
@@ -64687,14 +64708,15 @@ async function runCall(argv, out, deps) {
|
|
|
64687
64708
|
}
|
|
64688
64709
|
var callCommand = {
|
|
64689
64710
|
name: "call",
|
|
64690
|
-
synopsis: "sokie call <tool> [--input '<json>'] [--yes] [--preview <n> | --production] [--server <slug>] [--dir <dir>] [--site <url>]",
|
|
64711
|
+
synopsis: "sokie call <tool> [--input '<json>'] [--yes] [--preview <n> | --held <n> | --production] [--server <slug>] [--dir <dir>] [--site <url>]",
|
|
64691
64712
|
summary: "Call one tool as an agent would, on sokie dev or a deploy.",
|
|
64692
64713
|
help: `Calls the tool over MCP and prints what an agent would get back, then how
|
|
64693
64714
|
long it took. Exits 1 when the call fails.
|
|
64694
64715
|
|
|
64695
64716
|
By default the call goes to the sokie dev running for this checkout (start
|
|
64696
64717
|
it in another terminal first). --preview <n> calls the pull request's
|
|
64697
|
-
preview
|
|
64718
|
+
preview, --held <n> a deploy waiting for an owner or admin at its own
|
|
64719
|
+
address, and --production the live deploy, as you, with a token that lasts
|
|
64698
64720
|
five minutes; the call is logged with the client "Sokie test".
|
|
64699
64721
|
|
|
64700
64722
|
A tool that does more than read (write, delete or external) asks first,
|
|
@@ -64710,6 +64732,7 @@ called by name too, to see what an agent finds.
|
|
|
64710
64732
|
--input <json> The tool's arguments, as one JSON object (default {}).
|
|
64711
64733
|
--yes, -y Don't ask. Needed when there is no terminal, as in a script.
|
|
64712
64734
|
--preview <n> Call pull request n's preview deploy.
|
|
64735
|
+
--held <n> Call deploy n while it's held: sokie deploys lists it.
|
|
64713
64736
|
--production Call the live deploy.
|
|
64714
64737
|
--server <s> A server by slug, instead of the one this repository is linked to.
|
|
64715
64738
|
--dir <dir> The repository (default .).
|
|
@@ -64847,6 +64870,167 @@ async function exchangeDeployKey(info2, key, fetchFn = fetch) {
|
|
|
64847
64870
|
return body.access_token;
|
|
64848
64871
|
}
|
|
64849
64872
|
|
|
64873
|
+
// src/deploy-list.ts
|
|
64874
|
+
var ENDED_HOLDS = {
|
|
64875
|
+
HOLD_DECLINED: "declined",
|
|
64876
|
+
HOLD_WITHDRAWN: "withdrawn",
|
|
64877
|
+
HOLD_EXPIRED: "lapsed"
|
|
64878
|
+
};
|
|
64879
|
+
function statusOf(deploy) {
|
|
64880
|
+
if (deploy.status === "held")
|
|
64881
|
+
return { text: "held", tone: "warn" };
|
|
64882
|
+
if (deploy.status === "failed") {
|
|
64883
|
+
const ended = ENDED_HOLDS[deploy.error?.code ?? ""];
|
|
64884
|
+
return ended ? { text: ended, tone: "dim" } : { text: "failed", tone: "fail" };
|
|
64885
|
+
}
|
|
64886
|
+
if (deploy.status === "queued" || deploy.status === "building") {
|
|
64887
|
+
return { text: deploy.status, tone: "warn" };
|
|
64888
|
+
}
|
|
64889
|
+
if (deploy.previewPr !== undefined)
|
|
64890
|
+
return { text: "preview", tone: deploy.status === "live" ? "ok" : undefined };
|
|
64891
|
+
return deploy.status === "live" ? { text: "live", tone: "ok" } : { text: "replaced", tone: "dim" };
|
|
64892
|
+
}
|
|
64893
|
+
function sourceOf(deploy) {
|
|
64894
|
+
if (deploy.restoreOf !== undefined)
|
|
64895
|
+
return `restore of #${deploy.restoreOf}`;
|
|
64896
|
+
const source = deploy.source;
|
|
64897
|
+
if (!source)
|
|
64898
|
+
return "uploaded from the CLI";
|
|
64899
|
+
if (source.kind === "upload") {
|
|
64900
|
+
const edited = source.edited ?? [];
|
|
64901
|
+
if (edited.length > 0) {
|
|
64902
|
+
const named = edited.length <= 2 ? edited.join(" and ") : `${edited[0]} and ${edited.length - 1} more`;
|
|
64903
|
+
return `${named} edited in the app`;
|
|
64904
|
+
}
|
|
64905
|
+
return source.from === "web" ? "made in Sokie from a spec" : "uploaded from the CLI";
|
|
64906
|
+
}
|
|
64907
|
+
const pr = deploy.previewPr ?? source.prNumber;
|
|
64908
|
+
const sha = source.sha.slice(0, 7);
|
|
64909
|
+
return pr === undefined ? `${sha} on ${source.branch}` : `${sha} · preview #${pr}`;
|
|
64910
|
+
}
|
|
64911
|
+
var DAY_FORMAT = new Intl.DateTimeFormat("en", {
|
|
64912
|
+
month: "short",
|
|
64913
|
+
day: "numeric",
|
|
64914
|
+
timeZone: "UTC"
|
|
64915
|
+
});
|
|
64916
|
+
function onDay(at2) {
|
|
64917
|
+
return DAY_FORMAT.format(at2);
|
|
64918
|
+
}
|
|
64919
|
+
var MINUTE = 60000;
|
|
64920
|
+
var HOUR = 60 * MINUTE;
|
|
64921
|
+
var DAY = 24 * HOUR;
|
|
64922
|
+
function ago(at2, now) {
|
|
64923
|
+
const gone = Math.max(0, now - at2);
|
|
64924
|
+
if (gone < MINUTE)
|
|
64925
|
+
return "just now";
|
|
64926
|
+
if (gone < HOUR)
|
|
64927
|
+
return `${Math.floor(gone / MINUTE)} min ago`;
|
|
64928
|
+
if (gone < DAY)
|
|
64929
|
+
return `${Math.floor(gone / HOUR)} h ago`;
|
|
64930
|
+
if (gone < 2 * DAY)
|
|
64931
|
+
return "yesterday";
|
|
64932
|
+
if (gone < 30 * DAY)
|
|
64933
|
+
return `${Math.floor(gone / DAY)} days ago`;
|
|
64934
|
+
return new Date(at2).toLocaleDateString("en-US", {
|
|
64935
|
+
month: "short",
|
|
64936
|
+
day: "numeric",
|
|
64937
|
+
timeZone: "UTC"
|
|
64938
|
+
});
|
|
64939
|
+
}
|
|
64940
|
+
function headerLine(slug, facts) {
|
|
64941
|
+
if (facts.pin) {
|
|
64942
|
+
const since = facts.pin.skippedPushes === 1 ? "1 push" : `${facts.pin.skippedPushes} pushes`;
|
|
64943
|
+
return {
|
|
64944
|
+
text: `${slug} · production stays on #${facts.live ?? "?"} until sokie deploys resume (${since} since the restore)`,
|
|
64945
|
+
tone: "warn"
|
|
64946
|
+
};
|
|
64947
|
+
}
|
|
64948
|
+
if (facts.branch)
|
|
64949
|
+
return { text: `${slug} · production follows ${facts.branch}`, tone: "dim" };
|
|
64950
|
+
return { text: `${slug} · Sokie keeps its files`, tone: "dim" };
|
|
64951
|
+
}
|
|
64952
|
+
function rowLine(deploy, now) {
|
|
64953
|
+
const status = statusOf(deploy);
|
|
64954
|
+
const number4 = deploy.number === undefined ? "—" : `#${deploy.number}`;
|
|
64955
|
+
const text5 = ` ${number4.padEnd(5)} ${status.text.padEnd(9)} ${sourceOf(deploy).padEnd(32)} ${ago(deploy.createdAt, now)}`;
|
|
64956
|
+
return { text: text5, tone: status.tone === "dim" ? undefined : status.tone };
|
|
64957
|
+
}
|
|
64958
|
+
function heldLine(deploy) {
|
|
64959
|
+
const until = deploy.hold ? ` until ${onDay(deploy.hold.until)}` : "";
|
|
64960
|
+
const where = deploy.heldUrl ? ` · ${deploy.heldUrl}` : "";
|
|
64961
|
+
return { text: ` waiting for an admin${until}${where}`, tone: "dim" };
|
|
64962
|
+
}
|
|
64963
|
+
function listLines2(slug, deploys, facts) {
|
|
64964
|
+
const live = deploys.find((d) => d.status === "live" && d.previewPr === undefined)?.number;
|
|
64965
|
+
const lines = [headerLine(slug, { ...facts, live }), { text: "" }];
|
|
64966
|
+
if (deploys.length === 0) {
|
|
64967
|
+
lines.push({ text: " No deploys yet. sokie deploy makes the first one.", tone: "dim" });
|
|
64968
|
+
return lines;
|
|
64969
|
+
}
|
|
64970
|
+
for (const deploy of deploys) {
|
|
64971
|
+
lines.push(rowLine(deploy, facts.now));
|
|
64972
|
+
if (deploy.status === "held")
|
|
64973
|
+
lines.push(heldLine(deploy));
|
|
64974
|
+
}
|
|
64975
|
+
if (facts.more) {
|
|
64976
|
+
lines.push({
|
|
64977
|
+
text: ` … older ones too. sokie deploys --limit ${facts.limit * 2} goes further back; --json for scripts.`,
|
|
64978
|
+
tone: "dim"
|
|
64979
|
+
});
|
|
64980
|
+
}
|
|
64981
|
+
return lines;
|
|
64982
|
+
}
|
|
64983
|
+
function diffTone(line, index) {
|
|
64984
|
+
if (index === 0)
|
|
64985
|
+
return "strong";
|
|
64986
|
+
const sign = line.trimStart().slice(0, 2);
|
|
64987
|
+
if (sign === "+ ")
|
|
64988
|
+
return "ok";
|
|
64989
|
+
if (sign === "- ")
|
|
64990
|
+
return "fail";
|
|
64991
|
+
if (sign === "↑ " || sign === "↓ ")
|
|
64992
|
+
return "warn";
|
|
64993
|
+
return;
|
|
64994
|
+
}
|
|
64995
|
+
|
|
64996
|
+
// src/deploy-held.ts
|
|
64997
|
+
function names(list) {
|
|
64998
|
+
return list.length > 1 ? `${list.slice(0, -1).join(", ")} and ${list.at(-1)}` : list[0] ?? "";
|
|
64999
|
+
}
|
|
65000
|
+
function holdWhy(deploy, slug) {
|
|
65001
|
+
const tools = deploy.hold?.tools ?? [];
|
|
65002
|
+
if (deploy.hold?.reason === "newTools" && tools.length > 0) {
|
|
65003
|
+
const verbs = tools.length === 1 ? "deletes or acts" : "delete or act";
|
|
65004
|
+
return `It adds ${names(tools)}, which ${verbs} outside the API, so an owner or admin looks first.`;
|
|
65005
|
+
}
|
|
65006
|
+
return `Production deploys of ${slug} wait for an owner or admin.`;
|
|
65007
|
+
}
|
|
65008
|
+
function heldLines(slug, deploy) {
|
|
65009
|
+
const which = deploy.number === undefined ? "this deploy" : `deploy #${deploy.number}`;
|
|
65010
|
+
const lines = [
|
|
65011
|
+
{
|
|
65012
|
+
text: `Held: ${which} waits for an owner or admin before it goes on production.`,
|
|
65013
|
+
tone: "warn"
|
|
65014
|
+
},
|
|
65015
|
+
{ text: holdWhy(deploy, slug) }
|
|
65016
|
+
];
|
|
65017
|
+
if (deploy.heldUrl) {
|
|
65018
|
+
lines.push({ text: `The team can call it, signed in, at ${deploy.heldUrl}` });
|
|
65019
|
+
if (deploy.number !== undefined) {
|
|
65020
|
+
lines.push({
|
|
65021
|
+
text: `sokie call <tool> --held ${deploy.number} tries a tool there.`,
|
|
65022
|
+
tone: "dim"
|
|
65023
|
+
});
|
|
65024
|
+
}
|
|
65025
|
+
}
|
|
65026
|
+
const lapses = deploy.hold ? ` If no one does by ${onDay(deploy.hold.until)}, it lapses.` : "";
|
|
65027
|
+
lines.push({
|
|
65028
|
+
text: `Production stays as it is until an owner or admin puts it live; sokie requests lists it for them.${lapses}`,
|
|
65029
|
+
tone: "dim"
|
|
65030
|
+
});
|
|
65031
|
+
return lines;
|
|
65032
|
+
}
|
|
65033
|
+
|
|
64850
65034
|
// src/init/gitignore.ts
|
|
64851
65035
|
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
|
|
64852
65036
|
import { join as join8 } from "node:path";
|
|
@@ -64872,6 +65056,44 @@ function liveLines(server, deployment) {
|
|
|
64872
65056
|
const live = `Live: ${server.url ?? server.slug}${number4}`;
|
|
64873
65057
|
return server.liveDeploymentId ? [live, SWITCH_OVER_NOTE] : [live];
|
|
64874
65058
|
}
|
|
65059
|
+
async function reportDeploy(out, server, deployment) {
|
|
65060
|
+
const { status } = deployment;
|
|
65061
|
+
if (status === "failed") {
|
|
65062
|
+
for (const finding3 of deployment.findings ?? []) {
|
|
65063
|
+
await out.problem(findingLine(finding3), LEVEL_TONE[finding3.level]);
|
|
65064
|
+
}
|
|
65065
|
+
const reason = deployment.error?.message ?? "The deploy failed.";
|
|
65066
|
+
await out.result({ ok: false, status, server, deployment, error: deployment.error ?? null });
|
|
65067
|
+
await out.problem(`✗ ${reason}`, "fail");
|
|
65068
|
+
return EXIT.no;
|
|
65069
|
+
}
|
|
65070
|
+
const url2 = server.url;
|
|
65071
|
+
if (status === "held") {
|
|
65072
|
+
for (const line of heldLines(server.slug, deployment))
|
|
65073
|
+
await out.line(line.text, line.tone);
|
|
65074
|
+
const { heldUrl = null, hold = null } = deployment;
|
|
65075
|
+
await out.result({
|
|
65076
|
+
ok: true,
|
|
65077
|
+
status,
|
|
65078
|
+
server,
|
|
65079
|
+
deployment,
|
|
65080
|
+
url: url2,
|
|
65081
|
+
heldUrl,
|
|
65082
|
+
hold,
|
|
65083
|
+
linkFile: LINK_FILE
|
|
65084
|
+
});
|
|
65085
|
+
return deployment;
|
|
65086
|
+
}
|
|
65087
|
+
if (status === "live") {
|
|
65088
|
+
const [live, ...rest] = liveLines(server, deployment);
|
|
65089
|
+
if (live)
|
|
65090
|
+
await out.line(live, "ok");
|
|
65091
|
+
for (const line of rest)
|
|
65092
|
+
await out.line(line, "dim");
|
|
65093
|
+
}
|
|
65094
|
+
await out.result({ ok: true, status, server, deployment, url: url2, linkFile: LINK_FILE });
|
|
65095
|
+
return deployment;
|
|
65096
|
+
}
|
|
64875
65097
|
function platformFor(client) {
|
|
64876
65098
|
return {
|
|
64877
65099
|
query: client.query.bind(client),
|
|
@@ -64916,25 +65138,7 @@ async function run4(argv, out) {
|
|
|
64916
65138
|
progress: (status) => out.line(` ${status}`, "dim"),
|
|
64917
65139
|
...base === undefined || values.force ? {} : { base }
|
|
64918
65140
|
});
|
|
64919
|
-
|
|
64920
|
-
if (deployment.status === "failed") {
|
|
64921
|
-
for (const finding3 of deployment.findings ?? []) {
|
|
64922
|
-
await out.problem(findingLine(finding3), LEVEL_TONE[finding3.level]);
|
|
64923
|
-
}
|
|
64924
|
-
const reason = deployment.error?.message ?? "The deploy failed.";
|
|
64925
|
-
await out.result({ ok: false, server, deployment, error: deployment.error ?? null });
|
|
64926
|
-
await out.problem(`✗ ${reason}`, "fail");
|
|
64927
|
-
return EXIT.no;
|
|
64928
|
-
}
|
|
64929
|
-
if (deployment.status === "live") {
|
|
64930
|
-
const [live, ...rest] = liveLines(server, deployment);
|
|
64931
|
-
if (live)
|
|
64932
|
-
await out.line(live, "ok");
|
|
64933
|
-
for (const line of rest)
|
|
64934
|
-
await out.line(line, "dim");
|
|
64935
|
-
}
|
|
64936
|
-
await out.result({ ok: true, server, deployment, url: url2, linkFile: LINK_FILE });
|
|
64937
|
-
return deployment;
|
|
65141
|
+
return await reportDeploy(out, server, deployment);
|
|
64938
65142
|
};
|
|
64939
65143
|
const exit = (shipped) => typeof shipped === "number" ? shipped : EXIT.ok;
|
|
64940
65144
|
const deployKey = deployKeyFrom();
|
|
@@ -64987,7 +65191,7 @@ async function run4(argv, out) {
|
|
|
64987
65191
|
});
|
|
64988
65192
|
ensureIgnored(root, ".sokie/");
|
|
64989
65193
|
const shipped = await ship(platformFor(client), server, base);
|
|
64990
|
-
if (!github && typeof shipped !== "number" && shipped.number !== undefined) {
|
|
65194
|
+
if (!github && typeof shipped !== "number" && shipped.number !== undefined && shipped.status !== "held") {
|
|
64991
65195
|
writeLink(root, info2.site, {
|
|
64992
65196
|
serverId: server.serverId,
|
|
64993
65197
|
slug: server.slug,
|
|
@@ -65026,6 +65230,15 @@ sokie edit, a deploy is refused once production has moved on since the
|
|
|
65026
65230
|
files were downloaded (an edit in the app, a restore), so it never undoes
|
|
65027
65231
|
a change unseen: sokie pull brings that change in first.
|
|
65028
65232
|
|
|
65233
|
+
A server can make production wait for an owner or admin. A member's deploy
|
|
65234
|
+
that must wait (any, with the server's switch on; otherwise one that adds a
|
|
65235
|
+
delete or outside-the-API tool) builds, runs its cases and is held: it
|
|
65236
|
+
serves only the team, signed in, at its own address, for up to 7 days.
|
|
65237
|
+
Deploys from GitHub or CI wait only when the server says so. The command
|
|
65238
|
+
stops there, says why, where and until when, and exits 0; --json gives
|
|
65239
|
+
"status": "held" with "heldUrl" and "hold". sokie call --held <n> tries it,
|
|
65240
|
+
and an owner or admin puts it live from sokie requests.
|
|
65241
|
+
|
|
65029
65242
|
--no-wait Return once the deploy is queued.
|
|
65030
65243
|
--force Deploy these files even if production changed since they were downloaded.
|
|
65031
65244
|
${SITE_HELP}
|
|
@@ -65121,109 +65334,11 @@ function diffLines(view) {
|
|
|
65121
65334
|
return lines;
|
|
65122
65335
|
}
|
|
65123
65336
|
|
|
65124
|
-
// src/deploy-list.ts
|
|
65125
|
-
function statusOf(deploy) {
|
|
65126
|
-
if (deploy.status === "failed")
|
|
65127
|
-
return { text: "failed", tone: "fail" };
|
|
65128
|
-
if (deploy.status === "queued" || deploy.status === "building") {
|
|
65129
|
-
return { text: deploy.status, tone: "warn" };
|
|
65130
|
-
}
|
|
65131
|
-
if (deploy.previewPr !== undefined)
|
|
65132
|
-
return { text: "preview", tone: deploy.status === "live" ? "ok" : undefined };
|
|
65133
|
-
return deploy.status === "live" ? { text: "live", tone: "ok" } : { text: "replaced", tone: "dim" };
|
|
65134
|
-
}
|
|
65135
|
-
function sourceOf(deploy) {
|
|
65136
|
-
if (deploy.restoreOf !== undefined)
|
|
65137
|
-
return `restore of #${deploy.restoreOf}`;
|
|
65138
|
-
const source = deploy.source;
|
|
65139
|
-
if (!source)
|
|
65140
|
-
return "uploaded from the CLI";
|
|
65141
|
-
if (source.kind === "upload") {
|
|
65142
|
-
const edited = source.edited ?? [];
|
|
65143
|
-
if (edited.length > 0) {
|
|
65144
|
-
const named = edited.length <= 2 ? edited.join(" and ") : `${edited[0]} and ${edited.length - 1} more`;
|
|
65145
|
-
return `${named} edited in the app`;
|
|
65146
|
-
}
|
|
65147
|
-
return source.from === "web" ? "made in Sokie from a spec" : "uploaded from the CLI";
|
|
65148
|
-
}
|
|
65149
|
-
const pr = deploy.previewPr ?? source.prNumber;
|
|
65150
|
-
const sha = source.sha.slice(0, 7);
|
|
65151
|
-
return pr === undefined ? `${sha} on ${source.branch}` : `${sha} · preview #${pr}`;
|
|
65152
|
-
}
|
|
65153
|
-
var MINUTE = 60000;
|
|
65154
|
-
var HOUR = 60 * MINUTE;
|
|
65155
|
-
var DAY = 24 * HOUR;
|
|
65156
|
-
function ago(at2, now) {
|
|
65157
|
-
const gone = Math.max(0, now - at2);
|
|
65158
|
-
if (gone < MINUTE)
|
|
65159
|
-
return "just now";
|
|
65160
|
-
if (gone < HOUR)
|
|
65161
|
-
return `${Math.floor(gone / MINUTE)} min ago`;
|
|
65162
|
-
if (gone < DAY)
|
|
65163
|
-
return `${Math.floor(gone / HOUR)} h ago`;
|
|
65164
|
-
if (gone < 2 * DAY)
|
|
65165
|
-
return "yesterday";
|
|
65166
|
-
if (gone < 30 * DAY)
|
|
65167
|
-
return `${Math.floor(gone / DAY)} days ago`;
|
|
65168
|
-
return new Date(at2).toLocaleDateString("en-US", {
|
|
65169
|
-
month: "short",
|
|
65170
|
-
day: "numeric",
|
|
65171
|
-
timeZone: "UTC"
|
|
65172
|
-
});
|
|
65173
|
-
}
|
|
65174
|
-
function headerLine(slug, facts) {
|
|
65175
|
-
if (facts.pin) {
|
|
65176
|
-
const since = facts.pin.skippedPushes === 1 ? "1 push" : `${facts.pin.skippedPushes} pushes`;
|
|
65177
|
-
return {
|
|
65178
|
-
text: `${slug} · production stays on #${facts.live ?? "?"} until sokie deploys resume (${since} since the restore)`,
|
|
65179
|
-
tone: "warn"
|
|
65180
|
-
};
|
|
65181
|
-
}
|
|
65182
|
-
if (facts.branch)
|
|
65183
|
-
return { text: `${slug} · production follows ${facts.branch}`, tone: "dim" };
|
|
65184
|
-
return { text: `${slug} · Sokie keeps its files`, tone: "dim" };
|
|
65185
|
-
}
|
|
65186
|
-
function rowLine(deploy, now) {
|
|
65187
|
-
const status = statusOf(deploy);
|
|
65188
|
-
const number4 = deploy.number === undefined ? "—" : `#${deploy.number}`;
|
|
65189
|
-
const text5 = ` ${number4.padEnd(5)} ${status.text.padEnd(9)} ${sourceOf(deploy).padEnd(32)} ${ago(deploy.createdAt, now)}`;
|
|
65190
|
-
return { text: text5, tone: status.tone === "dim" ? undefined : status.tone };
|
|
65191
|
-
}
|
|
65192
|
-
function listLines2(slug, deploys, facts) {
|
|
65193
|
-
const live = deploys.find((d) => d.status === "live" && d.previewPr === undefined)?.number;
|
|
65194
|
-
const lines = [headerLine(slug, { ...facts, live }), { text: "" }];
|
|
65195
|
-
if (deploys.length === 0) {
|
|
65196
|
-
lines.push({ text: " No deploys yet. sokie deploy makes the first one.", tone: "dim" });
|
|
65197
|
-
return lines;
|
|
65198
|
-
}
|
|
65199
|
-
for (const deploy of deploys)
|
|
65200
|
-
lines.push(rowLine(deploy, facts.now));
|
|
65201
|
-
if (facts.more) {
|
|
65202
|
-
lines.push({
|
|
65203
|
-
text: ` … older ones too. sokie deploys --limit ${facts.limit * 2} goes further back; --json for scripts.`,
|
|
65204
|
-
tone: "dim"
|
|
65205
|
-
});
|
|
65206
|
-
}
|
|
65207
|
-
return lines;
|
|
65208
|
-
}
|
|
65209
|
-
function diffTone(line, index) {
|
|
65210
|
-
if (index === 0)
|
|
65211
|
-
return "strong";
|
|
65212
|
-
const sign = line.trimStart().slice(0, 2);
|
|
65213
|
-
if (sign === "+ ")
|
|
65214
|
-
return "ok";
|
|
65215
|
-
if (sign === "- ")
|
|
65216
|
-
return "fail";
|
|
65217
|
-
if (sign === "↑ " || sign === "↓ ")
|
|
65218
|
-
return "warn";
|
|
65219
|
-
return;
|
|
65220
|
-
}
|
|
65221
|
-
|
|
65222
65337
|
// src/deploys-restore.ts
|
|
65223
65338
|
var REFUSED = {
|
|
65224
65339
|
ALREADY_LIVE: (n) => `#${n} is what production runs now. sokie deploys lists the others.`,
|
|
65225
65340
|
DEPLOY_NOT_FOUND: (n) => `There is no deploy #${n} on this server.`,
|
|
65226
|
-
NOT_RESTORABLE: (n) => `#${n} never served production: it's a preview, it failed, or it hasn't finished. Only a deploy that went live and was replaced can be restored.`,
|
|
65341
|
+
NOT_RESTORABLE: (n) => `#${n} never served production: it's a preview, it failed, it was held and never put live, or it hasn't finished. Only a deploy that went live and was replaced can be restored.`,
|
|
65227
65342
|
DEPLOY_IN_PROGRESS: () => "A deploy is running. Try again when it finishes.",
|
|
65228
65343
|
MISSING_SECRETS: (n) => `#${n} needs a secret production no longer has.`,
|
|
65229
65344
|
PLAN_LIMIT_SERVERS: () => "This server is past your plan's server limit, so it takes no deploys. Delete a server or upgrade on the Billing page.",
|
|
@@ -65253,8 +65368,8 @@ async function restoreDeploy(out, client, server, n, options, deps) {
|
|
|
65253
65368
|
if (check3.refusal)
|
|
65254
65369
|
return await refused2(out, new ConvexError(check3.refusal), n) ?? EXIT.no;
|
|
65255
65370
|
if (check3.missingSecrets.length > 0) {
|
|
65256
|
-
const
|
|
65257
|
-
return fail(out, "missing_secrets", `#${n} reads ${
|
|
65371
|
+
const names2 = check3.missingSecrets.join(", ");
|
|
65372
|
+
return fail(out, "missing_secrets", `#${n} reads ${names2}, which production doesn't have any more. Set it with sokie secrets set ${check3.missingSecrets[0]}, or restore a later deploy.`, EXIT.no);
|
|
65258
65373
|
}
|
|
65259
65374
|
const page = await client.query(fn.server, { serverId }).catch(() => null);
|
|
65260
65375
|
const live = page?.live?.number;
|
|
@@ -65293,6 +65408,18 @@ async function restoreDeploy(out, client, server, n, options, deps) {
|
|
|
65293
65408
|
});
|
|
65294
65409
|
return EXIT.no;
|
|
65295
65410
|
}
|
|
65411
|
+
if (done.status === "held") {
|
|
65412
|
+
for (const line of heldLines(server.slug, done))
|
|
65413
|
+
await out.line(line.text, line.tone);
|
|
65414
|
+
const { status, heldUrl = null, hold = null } = done;
|
|
65415
|
+
await out.result({
|
|
65416
|
+
ok: true,
|
|
65417
|
+
server: server.slug,
|
|
65418
|
+
deploy: { number: queued.number, restoreOf: n, status, heldUrl, hold },
|
|
65419
|
+
pinned: queued.pinned
|
|
65420
|
+
});
|
|
65421
|
+
return EXIT.ok;
|
|
65422
|
+
}
|
|
65296
65423
|
const took = Math.max(1, Math.round((Date.now() - started) / 1000));
|
|
65297
65424
|
await out.line(`#${queued.number} is live in ${took} s · ${server.url ?? server.slug}`, "ok");
|
|
65298
65425
|
await out.line("Calls can reach the previous deploy for up to a minute while the edges catch up.", "dim");
|
|
@@ -65467,7 +65594,9 @@ var deploysCommand = {
|
|
|
65467
65594
|
summary: "List a server's deploys, compare two, or restore an earlier one.",
|
|
65468
65595
|
help: `With no verb, lists the newest deploys (--limit, default 10): each one's
|
|
65469
65596
|
number, status, where its files came from and when, under what production
|
|
65470
|
-
follows now.
|
|
65597
|
+
follows now. A held deploy, waiting for an owner or admin, shows until when
|
|
65598
|
+
and the address the team can call it at; one that never went live says
|
|
65599
|
+
whether it was declined, withdrawn or lapsed.
|
|
65471
65600
|
|
|
65472
65601
|
diff [<from>] <to>
|
|
65473
65602
|
Compares two deploys' bundles and prints what an agent sees differently:
|
|
@@ -66336,19 +66465,19 @@ async function followWorkspace(out, client, deps) {
|
|
|
66336
66465
|
return null;
|
|
66337
66466
|
}
|
|
66338
66467
|
async function followNames(out, client, domain2, deps, left) {
|
|
66339
|
-
let
|
|
66468
|
+
let names2 = [];
|
|
66340
66469
|
for (let waited = 0;waited < left; waited += POLL_MS2) {
|
|
66341
|
-
|
|
66342
|
-
if (
|
|
66343
|
-
const list =
|
|
66344
|
-
await out.line(` ● ${
|
|
66470
|
+
names2 = (await allHostnames(client)).filter((h) => h.hostname.endsWith(`.${domain2}`));
|
|
66471
|
+
if (names2.length > 0 && names2.every((h) => h.status === "live")) {
|
|
66472
|
+
const list = names2.map((h) => h.serverName).join(", ");
|
|
66473
|
+
await out.line(` ● ${names2.length} server${names2.length === 1 ? "" : "s"} live under it: ${list}`, "ok");
|
|
66345
66474
|
await out.line("New servers get theirs as they are made.", "dim");
|
|
66346
|
-
return
|
|
66475
|
+
return names2;
|
|
66347
66476
|
}
|
|
66348
66477
|
await deps.sleep(POLL_MS2);
|
|
66349
66478
|
}
|
|
66350
66479
|
await out.line("Servers are still getting their certificates; sokie domains shows each.", "dim");
|
|
66351
|
-
return
|
|
66480
|
+
return names2;
|
|
66352
66481
|
}
|
|
66353
66482
|
async function followExact(out, client, server, deps) {
|
|
66354
66483
|
let said = "";
|
|
@@ -66398,8 +66527,8 @@ async function addDomain(out, client, domain2, options, deps) {
|
|
|
66398
66527
|
return EXIT.ok;
|
|
66399
66528
|
}
|
|
66400
66529
|
await out.line("Waiting for both (Ctrl+C stops waiting; Sokie keeps checking)…", "dim");
|
|
66401
|
-
const
|
|
66402
|
-
await out.result({ ok: true, domain: added2, dns: dns2, ...
|
|
66530
|
+
const names2 = await followWorkspace(out, client, deps);
|
|
66531
|
+
await out.result({ ok: true, domain: added2, dns: dns2, ...names2 ? { hostnames: names2 } : {} });
|
|
66403
66532
|
return EXIT.ok;
|
|
66404
66533
|
}
|
|
66405
66534
|
const added = await client.mutation(domainFn.addExact, { serverId: server.serverId, domain: domain2 });
|
|
@@ -67859,9 +67988,8 @@ var requestFn = {
|
|
|
67859
67988
|
};
|
|
67860
67989
|
|
|
67861
67990
|
// src/requests.ts
|
|
67862
|
-
var day = new Intl.DateTimeFormat("en", { month: "short", day: "numeric", timeZone: "UTC" });
|
|
67863
67991
|
function lapsesOn(request) {
|
|
67864
|
-
return
|
|
67992
|
+
return onDay(request.expiresAt);
|
|
67865
67993
|
}
|
|
67866
67994
|
async function allRequests(client, list) {
|
|
67867
67995
|
const rows = [];
|
|
@@ -67938,8 +68066,9 @@ function rowLines(request, short, headline, now) {
|
|
|
67938
68066
|
const lines = [{ text: ` ${id} ${headline}` }];
|
|
67939
68067
|
if (request.reason)
|
|
67940
68068
|
lines.push({ text: `${indent2}${quoted(request.reason)}` });
|
|
68069
|
+
const since = request.change.kind === "deploy.promote" ? "held" : "asked";
|
|
67941
68070
|
lines.push({
|
|
67942
|
-
text: `${indent2}
|
|
68071
|
+
text: `${indent2}${since} ${ago(request.createdAt, now)} · lapses ${lapsesOn(request)}`,
|
|
67943
68072
|
tone: "dim"
|
|
67944
68073
|
});
|
|
67945
68074
|
return lines;
|
|
@@ -67976,12 +68105,55 @@ function listLines3(waiting, mine, now) {
|
|
|
67976
68105
|
lines.push({ text: "" }, { text: `sokie requests ${hints.join("; ")}.`, tone: "dim" });
|
|
67977
68106
|
return lines;
|
|
67978
68107
|
}
|
|
68108
|
+
var AUTOMATED = new Set(["GitHub", "A CI deploy key"]);
|
|
68109
|
+
function emailed(request) {
|
|
68110
|
+
return !(request.change.kind === "deploy.promote" && AUTOMATED.has(request.requesterName));
|
|
68111
|
+
}
|
|
68112
|
+
function answeredLines(request, outcome, noted) {
|
|
68113
|
+
const { requesterName: who, words: words2, change } = request;
|
|
68114
|
+
const person = emailed(request);
|
|
68115
|
+
const hears = person ? ` ${who} hears by email.` : "";
|
|
68116
|
+
const theyHear = person ? `They hear by email${noted ? ", with your note" : ""}.` : "";
|
|
68117
|
+
const promote = change.kind === "deploy.promote";
|
|
68118
|
+
if (outcome === "approved") {
|
|
68119
|
+
return [
|
|
68120
|
+
{ text: `Approved: ${words2}.`, tone: "ok" },
|
|
68121
|
+
{
|
|
68122
|
+
text: promote ? `It builds again with the secrets set now, then goes live; sokie deploys shows it.${hears}` : `Sokie made the change as you, and ${who} hears by email.`,
|
|
68123
|
+
tone: "dim"
|
|
68124
|
+
}
|
|
68125
|
+
];
|
|
68126
|
+
}
|
|
68127
|
+
if (outcome === "declined") {
|
|
68128
|
+
return [
|
|
68129
|
+
{ text: `Declined ${who}'s request to ${words2}.`, tone: "ok" },
|
|
68130
|
+
{
|
|
68131
|
+
text: promote ? `Its held address stops answering. ${theyHear}`.trim() : theyHear,
|
|
68132
|
+
tone: "dim"
|
|
68133
|
+
}
|
|
68134
|
+
];
|
|
68135
|
+
}
|
|
68136
|
+
return [
|
|
68137
|
+
{
|
|
68138
|
+
text: `Closed it: the request to ${words2} no longer applied, so nothing changed.`,
|
|
68139
|
+
tone: "warn"
|
|
68140
|
+
},
|
|
68141
|
+
{
|
|
68142
|
+
text: promote ? `Deploy #${change.number} isn't held any more; sokie deploys says what became of it.${hears}` : `The server is gone, or the change was made another way.${hears}`,
|
|
68143
|
+
tone: "dim"
|
|
68144
|
+
}
|
|
68145
|
+
];
|
|
68146
|
+
}
|
|
67979
68147
|
function refused4(out, error62, gone) {
|
|
67980
68148
|
if (!(error62 instanceof ConvexError))
|
|
67981
68149
|
return null;
|
|
67982
68150
|
const data = error62.data;
|
|
67983
68151
|
if (data === "NOT_FOUND")
|
|
67984
68152
|
return fail(out, gone.code, gone.message, EXIT.no);
|
|
68153
|
+
if (data === "DEPLOY_IN_PROGRESS") {
|
|
68154
|
+
const message2 = "Another production deploy of this server is still running. Approve again once it finishes; sokie deploys shows it.";
|
|
68155
|
+
return fail(out, "deploy_in_progress", message2, EXIT.no);
|
|
68156
|
+
}
|
|
67985
68157
|
if (typeof data !== "object" || data === null)
|
|
67986
68158
|
return null;
|
|
67987
68159
|
const { code: code2, message } = data;
|
|
@@ -68069,15 +68241,8 @@ async function answer(out, client, given, how, deps) {
|
|
|
68069
68241
|
approve: how.approve,
|
|
68070
68242
|
...how.note ? { note: how.note } : {}
|
|
68071
68243
|
});
|
|
68072
|
-
|
|
68073
|
-
await out.line(
|
|
68074
|
-
await out.line(`Sokie made the change as you, and ${request.requesterName} hears by email.`, "dim");
|
|
68075
|
-
} else if (outcome === "declined") {
|
|
68076
|
-
await out.line(`Declined ${request.requesterName}'s request to ${request.words}.`, "ok");
|
|
68077
|
-
await out.line(`They hear by email${how.note ? ", with your note" : ""}.`, "dim");
|
|
68078
|
-
} else {
|
|
68079
|
-
await out.line(`Closed it: the request to ${request.words} no longer applied, so nothing changed.`, "warn");
|
|
68080
|
-
await out.line(`The server is gone, or the change was made another way. ${request.requesterName} hears by email.`, "dim");
|
|
68244
|
+
for (const line of answeredLines(request, outcome, how.note !== undefined)) {
|
|
68245
|
+
await out.line(line.text, line.tone);
|
|
68081
68246
|
}
|
|
68082
68247
|
await out.result({ ok: true, requestId: request.requestId, outcome, request });
|
|
68083
68248
|
return EXIT.ok;
|
|
@@ -68088,6 +68253,9 @@ async function withdraw(out, client, given) {
|
|
|
68088
68253
|
return request;
|
|
68089
68254
|
await client.mutation(requestFn.withdraw, { requestId: request.requestId });
|
|
68090
68255
|
await out.line(`Withdrew your request to ${request.words}.`, "ok");
|
|
68256
|
+
if (request.change.kind === "deploy.promote") {
|
|
68257
|
+
await out.line("That ends the held deploy: its held address stops answering.", "dim");
|
|
68258
|
+
}
|
|
68091
68259
|
await out.result({ ok: true, requestId: request.requestId, outcome: "withdrawn", request });
|
|
68092
68260
|
return EXIT.ok;
|
|
68093
68261
|
}
|
|
@@ -68153,6 +68321,11 @@ it's live, and moving a live name under the workspace domain are for owners
|
|
|
68153
68321
|
and admins. A member asks instead; any one owner or admin approves, which
|
|
68154
68322
|
makes the change as them, or declines. A request waits 7 days, then lapses.
|
|
68155
68323
|
|
|
68324
|
+
A held deploy asks too: when a server makes production wait for an owner or
|
|
68325
|
+
admin, a deploy that must wait (sokie deploy says so) is listed here as a
|
|
68326
|
+
request to put it on production. Approving builds it again with the secrets
|
|
68327
|
+
set now and puts it live; declining or withdrawing ends it.
|
|
68328
|
+
|
|
68156
68329
|
With no verb, lists the requests waiting for your answer (as an owner or
|
|
68157
68330
|
admin) and the ones you asked, in the workspace commands work in: each one's
|
|
68158
68331
|
id, who asked for what and why, and when it lapses.
|
|
@@ -68166,7 +68339,7 @@ decline <id>
|
|
|
68166
68339
|
--note <text> A word back to them, up to 500 characters.
|
|
68167
68340
|
|
|
68168
68341
|
withdraw <id>
|
|
68169
|
-
Takes back a request you asked.
|
|
68342
|
+
Takes back a request you asked; for your held deploy, that ends it.
|
|
68170
68343
|
|
|
68171
68344
|
ask <kind> <server> [<new-name>]
|
|
68172
68345
|
Asks the workspace's owners and admins, who hear by email. <server> is the
|
|
@@ -68227,8 +68400,8 @@ async function run10(argv, out) {
|
|
|
68227
68400
|
cursor = page2.continueCursor;
|
|
68228
68401
|
}
|
|
68229
68402
|
for (const secret of secrets) {
|
|
68230
|
-
const
|
|
68231
|
-
await out.line(`${secret.name} ${out.tint("dim", `set ${
|
|
68403
|
+
const day = new Date(secret.updatedAt).toISOString().slice(0, 10);
|
|
68404
|
+
await out.line(`${secret.name} ${out.tint("dim", `set ${day}`)}`);
|
|
68232
68405
|
}
|
|
68233
68406
|
if (secrets.length === 0)
|
|
68234
68407
|
await out.line(`${server.slug} has no secrets set.`);
|
|
@@ -68317,7 +68490,7 @@ function caseJson(run11) {
|
|
|
68317
68490
|
var TERMINAL4 = { call: callTool, signedIn: withSignedIn, now: Date.now };
|
|
68318
68491
|
var RENEW_MS = 30000;
|
|
68319
68492
|
function caseTarget(target) {
|
|
68320
|
-
return target.kind;
|
|
68493
|
+
return target.kind === "held" ? "production" : target.kind;
|
|
68321
68494
|
}
|
|
68322
68495
|
async function runCases(out, deps, plan, target, endpointFor, all) {
|
|
68323
68496
|
const first = await endpointFor();
|
|
@@ -68381,7 +68554,7 @@ async function runTest(argv, out, deps) {
|
|
|
68381
68554
|
if (positionals.length > 0) {
|
|
68382
68555
|
throw new UsageError("sokie test runs every case; it takes no names. --dir picks the repository.");
|
|
68383
68556
|
}
|
|
68384
|
-
const target = targetFrom(values
|
|
68557
|
+
const target = targetFrom(values);
|
|
68385
68558
|
if (target.kind === "dev" && values.server !== undefined) {
|
|
68386
68559
|
throw new UsageError("--server picks a deploy: add --preview <n> or --production.");
|
|
68387
68560
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abyssinia-labs/sokie",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"description": "Turn an OpenAPI or Swagger spec into an MCP server, run it on your machine, and deploy it to Sokie. The sokie command (alias sok).",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"homepage": "https://sokie.sh/docs/cli",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"author": "Abyssinia Labs <hello@sokie.sh> (https://sokie.sh)",
|
|
12
12
|
"sokie": {
|
|
13
|
-
"site": "https://
|
|
13
|
+
"site": "https://sokie.sh"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"mcp",
|