@lotics/cli 0.204.0 → 0.205.1
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/AGENTS.md +35 -0
- package/README.md +17 -1
- package/dist/src/cli.js +340 -53
- package/dist/src/client.d.ts +88 -3
- package/dist/src/client.js +63 -13
- package/docs/cli_reference.md +9 -7
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -14940,10 +14940,12 @@ var LoticsClient = class {
|
|
|
14940
14940
|
body = {};
|
|
14941
14941
|
}
|
|
14942
14942
|
const message2 = typeof body.message === "string" ? body.message : text;
|
|
14943
|
+
const hint = typeof body.hint === "string" ? `
|
|
14944
|
+
${body.hint}` : "";
|
|
14943
14945
|
const trace = requestId === void 0 ? "" : `
|
|
14944
14946
|
|
|
14945
14947
|
Request id: ${requestId} \u2014 quote this to Lotics support.`;
|
|
14946
|
-
throw new LoticsRequestError(`${response.status}: ${message2}${trace}`, response.status, body);
|
|
14948
|
+
throw new LoticsRequestError(`${response.status}: ${message2}${hint}${trace}`, response.status, body);
|
|
14947
14949
|
}
|
|
14948
14950
|
/**
|
|
14949
14951
|
* The backend's `log()` middleware registers `user-agent`,
|
|
@@ -14993,9 +14995,23 @@ var LoticsClient = class {
|
|
|
14993
14995
|
if (!response.ok) await this.throwResponseError(response, headers["x-request-id"]);
|
|
14994
14996
|
return response.json();
|
|
14995
14997
|
}
|
|
14998
|
+
/** `email` is null for a credential no person signs in as; `account_type` is absent from an older server. */
|
|
14999
|
+
/** `credential_origin` describes the CREDENTIAL this call carried, not the
|
|
15000
|
+
* member above: `sign_in` for a terminal somebody signed in from, `api_key`
|
|
15001
|
+
* for one an admin issued. Null for a row minted before it was recorded, and
|
|
15002
|
+
* absent from an older server — both mean not told. */
|
|
14996
15003
|
async whoami() {
|
|
14997
15004
|
return this.request("GET", "/v1/cli/whoami");
|
|
14998
15005
|
}
|
|
15006
|
+
/**
|
|
15007
|
+
* Hand this client's own credential back — what `lotics auth logout` calls
|
|
15008
|
+
* before it forgets the key, so signing out ends the credential instead of
|
|
15009
|
+
* leaving a live one on a server nobody can see it from. Takes no id: the
|
|
15010
|
+
* only thing it can revoke is the key these requests carry.
|
|
15011
|
+
*/
|
|
15012
|
+
async revokeSelfCredential() {
|
|
15013
|
+
return this.request("DELETE", "/v1/api_keys/self");
|
|
15014
|
+
}
|
|
14999
15015
|
/**
|
|
15000
15016
|
* File a hand-written report, or a list of them in one call. Unlike a
|
|
15001
15017
|
* telemetry flush, the headers this stamps are CORRECT: the invocation making
|
|
@@ -15191,8 +15207,12 @@ var LoticsClient = class {
|
|
|
15191
15207
|
* provenance is a 400, and one already on the latest release a 409 — both
|
|
15192
15208
|
* refusals, both before any write. Admin-only.
|
|
15193
15209
|
*/
|
|
15194
|
-
async upgradeApp(app_id) {
|
|
15195
|
-
return this.request(
|
|
15210
|
+
async upgradeApp(app_id, opts = {}) {
|
|
15211
|
+
return this.request(
|
|
15212
|
+
"POST",
|
|
15213
|
+
`/v1/apps/${encodeURIComponent(app_id)}/upgrade`,
|
|
15214
|
+
opts.acknowledge_breaking_api_change ? { acknowledge_breaking_api_change: true } : {}
|
|
15215
|
+
);
|
|
15196
15216
|
}
|
|
15197
15217
|
/**
|
|
15198
15218
|
* Capture live records from this workspace as a starter's sample data.
|
|
@@ -15386,6 +15406,36 @@ var LoticsClient = class {
|
|
|
15386
15406
|
{ public_subdomain }
|
|
15387
15407
|
);
|
|
15388
15408
|
}
|
|
15409
|
+
/**
|
|
15410
|
+
* Publish the app's API — the owner's promise about what its declared queries
|
|
15411
|
+
* and workflows return to a caller outside the app.
|
|
15412
|
+
*
|
|
15413
|
+
* Snapshots the contract and answers the version it took, plus what the
|
|
15414
|
+
* published surface exposes that the owner may not have intended. A query
|
|
15415
|
+
* that does not name its columns is refused (400) before anything is written:
|
|
15416
|
+
* the app cannot promise field names it never stated.
|
|
15417
|
+
*/
|
|
15418
|
+
async publishAppApi(app_id) {
|
|
15419
|
+
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/api/publish`, {});
|
|
15420
|
+
}
|
|
15421
|
+
/** End the promise. `unpublished: false` means the app was publishing nothing,
|
|
15422
|
+
* which this leaves unchanged. The superseded snapshot stays, so a later
|
|
15423
|
+
* publish continues the numbering rather than reusing a version. */
|
|
15424
|
+
async unpublishAppApi(app_id) {
|
|
15425
|
+
return this.request("DELETE", `/v1/apps/${encodeURIComponent(app_id)}/api/publish`);
|
|
15426
|
+
}
|
|
15427
|
+
/** Whether the app is publishing an API, and which contract version. */
|
|
15428
|
+
async getAppApiPublication(app_id) {
|
|
15429
|
+
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/api/publication`);
|
|
15430
|
+
}
|
|
15431
|
+
/**
|
|
15432
|
+
* The published API as an OpenAPI 3.1 document — what a consumer's own
|
|
15433
|
+
* generator reads. Rendered from the live SNAPSHOT rather than the manifest,
|
|
15434
|
+
* so it describes what the app has promised; 404 while nothing is published.
|
|
15435
|
+
*/
|
|
15436
|
+
async getAppOpenApiDocument(app_id) {
|
|
15437
|
+
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/openapi.json`);
|
|
15438
|
+
}
|
|
15389
15439
|
async getAppVersion(app_id, version_id) {
|
|
15390
15440
|
return this.request(
|
|
15391
15441
|
"GET",
|
|
@@ -15475,7 +15525,10 @@ var LoticsClient = class {
|
|
|
15475
15525
|
...body.outputs ? { outputs: body.outputs } : {},
|
|
15476
15526
|
...body.name ? { name: body.name } : {},
|
|
15477
15527
|
...body.description ? { description: body.description } : {},
|
|
15478
|
-
...body.expected_body_sha ? { expected_body_sha: body.expected_body_sha } : {}
|
|
15528
|
+
...body.expected_body_sha ? { expected_body_sha: body.expected_body_sha } : {},
|
|
15529
|
+
// Sent only when the caller asked for it: absent means "refuse a break",
|
|
15530
|
+
// which is the answer a caller who said nothing gave.
|
|
15531
|
+
...body.acknowledge_breaking_api_change ? { acknowledge_breaking_api_change: true } : {}
|
|
15479
15532
|
});
|
|
15480
15533
|
}
|
|
15481
15534
|
/**
|
|
@@ -15486,12 +15539,13 @@ var LoticsClient = class {
|
|
|
15486
15539
|
* deploy validates the manifest. Note: `apps.queries` is manifest-authoritative,
|
|
15487
15540
|
* so the next `lotics app deploy` overwrites this from the manifest.
|
|
15488
15541
|
*/
|
|
15489
|
-
async setAppQuery(app_id, alias, declaration, expected_sha) {
|
|
15542
|
+
async setAppQuery(app_id, alias, declaration, expected_sha, acknowledge_breaking_api_change) {
|
|
15490
15543
|
return this.execute("set_app_query", {
|
|
15491
15544
|
app_id,
|
|
15492
15545
|
alias,
|
|
15493
15546
|
declaration,
|
|
15494
|
-
...expected_sha ? { expected_sha } : {}
|
|
15547
|
+
...expected_sha ? { expected_sha } : {},
|
|
15548
|
+
...acknowledge_breaking_api_change ? { acknowledge_breaking_api_change: true } : {}
|
|
15495
15549
|
});
|
|
15496
15550
|
}
|
|
15497
15551
|
/**
|
|
@@ -15694,16 +15748,12 @@ var LoticsClient = class {
|
|
|
15694
15748
|
formData.append("workflow_aliases", JSON.stringify(args.workflow_aliases ?? []));
|
|
15695
15749
|
formData.append("agent_aliases", JSON.stringify(args.agent_aliases ?? []));
|
|
15696
15750
|
formData.append("query_aliases", JSON.stringify(args.query_aliases ?? []));
|
|
15751
|
+
if (args.acknowledge_breaking_api_change) {
|
|
15752
|
+
formData.append("acknowledge_breaking_api_change", "true");
|
|
15753
|
+
}
|
|
15697
15754
|
const url2 = `${this.baseUrl}/v1/apps/${encodeURIComponent(args.app_id)}/versions`;
|
|
15698
15755
|
const headers = this.buildHeaders();
|
|
15699
15756
|
const response = await fetch(url2, { method: "POST", headers, body: formData });
|
|
15700
|
-
if (response.status === 409) {
|
|
15701
|
-
const conflict = await response.json();
|
|
15702
|
-
const err = new Error(conflict.message ?? "Deploy conflict \u2014 pull required");
|
|
15703
|
-
err.code = "VERSION_CONFLICT";
|
|
15704
|
-
err.current_version_id = conflict.current_version_id ?? null;
|
|
15705
|
-
throw err;
|
|
15706
|
-
}
|
|
15707
15757
|
if (!response.ok) {
|
|
15708
15758
|
await this.throwResponseError(response, headers["x-request-id"]);
|
|
15709
15759
|
}
|
|
@@ -16432,6 +16482,7 @@ function upsertProfile(orgId, fields) {
|
|
|
16432
16482
|
const workspace_id = fields.workspace_id ?? existing?.workspace_id;
|
|
16433
16483
|
const profile = {
|
|
16434
16484
|
api_key: fields.api_key,
|
|
16485
|
+
kind: fields.kind,
|
|
16435
16486
|
org_name: existing?.org_name ?? fields.org_name,
|
|
16436
16487
|
...existing?.server_name === void 0 ? {} : { server_name: existing.server_name },
|
|
16437
16488
|
api_url: fields.api_url,
|
|
@@ -34139,6 +34190,9 @@ function capabilityDescriptionRefusal(kind, alias, description) {
|
|
|
34139
34190
|
const plural = kind === "query" ? "queries" : "workflows";
|
|
34140
34191
|
return `${noun} alias "${alias}" description is ${description.length} characters \u2014 the cap is ${MAX_APP_CAPABILITY_DESCRIPTION}. It is a one-line summary an agent reads when choosing between this app's ${plural}; longer guidance belongs in the agent's own instructions.`;
|
|
34141
34192
|
}
|
|
34193
|
+
var APP_ANONYMOUS_REQUEST_MAX_BYTES = 256 * 1024;
|
|
34194
|
+
var PUBLIC_APP_UPLOAD_BYTES_LIMIT = 500 * 1024 * 1024;
|
|
34195
|
+
var PUBLIC_APP_UPLOAD_BYTES_WINDOW_SECONDS = 60 * 60;
|
|
34142
34196
|
var appWorkflowDeclarationSchema = zod_default.object({
|
|
34143
34197
|
workflow_id: zod_default.string().describe("ID of the workflow this alias resolves to"),
|
|
34144
34198
|
inputs: zod_default.record(zod_default.string(), appWorkflowInputSchema).optional().describe(
|
|
@@ -34249,6 +34303,9 @@ var appSchema = zod_default.object({
|
|
|
34249
34303
|
"Alias \u2192 agent declaration map. Each alias binds a streaming tool-loop agent the app runs via `useAgentRun(alias)` (an SSE stream), with declared tools, model, and typed inputs/outputs. Synced from the app's `lotics.agents` manifest on every deploy. The agent runs under the app's IAM principal; runs persist a flat history per session."
|
|
34250
34304
|
),
|
|
34251
34305
|
theme: appThemeSchema.nullable().optional().describe("Theme settings for the app"),
|
|
34306
|
+
can_author: zod_default.boolean().optional().describe(
|
|
34307
|
+
"Whether the caller may author this app \u2014 open a draft, edit its source, publish it. True for the app's owner and for an organization admin. A `manager` share is not authoring: it manages the app under its owner's authority, which is somebody else's. Optional because an older server omits it \u2014 absent means that server's rule, an organization admin."
|
|
34308
|
+
),
|
|
34252
34309
|
/**
|
|
34253
34310
|
* The starter this app is the ORIGIN of, when it is one — list enrichment,
|
|
34254
34311
|
* read from the registry rather than stored on the app.
|
|
@@ -40054,7 +40111,7 @@ function resultSideEffects(result) {
|
|
|
40054
40111
|
}
|
|
40055
40112
|
|
|
40056
40113
|
// src/version.ts
|
|
40057
|
-
var VERSION = "0.
|
|
40114
|
+
var VERSION = "0.205.1";
|
|
40058
40115
|
|
|
40059
40116
|
// src/timezone.ts
|
|
40060
40117
|
function machineTimezone() {
|
|
@@ -40631,6 +40688,11 @@ var COMMANDS = [
|
|
|
40631
40688
|
" --prune-invoked <alias> unbinds a workflow the",
|
|
40632
40689
|
" workspace has already RUN \u2014 named per alias, only",
|
|
40633
40690
|
" when you know that caller is gone)",
|
|
40691
|
+
" [--acknowledge-breaking-api] \u2026 even though it breaks what the app's published API",
|
|
40692
|
+
" promises. Without it such a release is refused and",
|
|
40693
|
+
" every breaking change is named. It counts only from",
|
|
40694
|
+
" an organization admin. Same flag on app query set,",
|
|
40695
|
+
" app workflow set and app upgrade",
|
|
40634
40696
|
" lotics app versions [app_id] Show deploy history newest-first (version,",
|
|
40635
40697
|
" timestamp, deployer, build status, -m message;",
|
|
40636
40698
|
" * marks the currently served version)",
|
|
@@ -40643,6 +40705,23 @@ var COMMANDS = [
|
|
|
40643
40705
|
" Already on the latest version says so and does",
|
|
40644
40706
|
" nothing; an app nobody copied has no package to",
|
|
40645
40707
|
" offer one",
|
|
40708
|
+
" lotics app api publish [app_id] Publish the app's API \u2014 the promise its declared",
|
|
40709
|
+
" queries and workflows make to a caller outside it",
|
|
40710
|
+
" (a customer's own site or server), snapshotted as a",
|
|
40711
|
+
" contract version. Prints that version, when it was",
|
|
40712
|
+
" taken, and what the published surface exposes. A",
|
|
40713
|
+
" query that does not name the columns it returns is",
|
|
40714
|
+
" refused: those field names are not the app's to keep",
|
|
40715
|
+
" lotics app api unpublish [app_id] End the promise. A later publish continues the",
|
|
40716
|
+
" numbering rather than reusing a version",
|
|
40717
|
+
" lotics app api status [app_id] Whether an API is published, and which contract",
|
|
40718
|
+
" version its callers hold",
|
|
40719
|
+
" lotics app api spec [app_id] [-o <file.json>]",
|
|
40720
|
+
" The OpenAPI document of the published API, rendered",
|
|
40721
|
+
" from the snapshot \u2014 what a consumer's own generator",
|
|
40722
|
+
" reads. To stdout, or to the file named",
|
|
40723
|
+
" [--json] publish / unpublish / status answer with one object",
|
|
40724
|
+
" on stdout and nothing else; every warning rides in it",
|
|
40646
40725
|
" lotics app codegen [path] Regenerate .lotics/* (types + field/option ids)",
|
|
40647
40726
|
" from the manifest + workspace schema \u2014 no deploy",
|
|
40648
40727
|
" lotics app check [--screens] Run every deploy pre-flight WITHOUT building or",
|
|
@@ -44763,7 +44842,9 @@ function warn(text) {
|
|
|
44763
44842
|
console.error(text);
|
|
44764
44843
|
}
|
|
44765
44844
|
function emitJson(value) {
|
|
44766
|
-
const
|
|
44845
|
+
const own = "warnings" in value && Array.isArray(value.warnings) ? value.warnings : [];
|
|
44846
|
+
const warnings = [...collectedWarnings, ...own];
|
|
44847
|
+
const payload = warnings.length > 0 ? { ...value, warnings } : value;
|
|
44767
44848
|
collectedWarnings.length = 0;
|
|
44768
44849
|
console.log(JSON.stringify(payload, null, 2));
|
|
44769
44850
|
}
|
|
@@ -46228,6 +46309,12 @@ function deepLinkLoc(pathname, search) {
|
|
|
46228
46309
|
if (pathname === "/" || pathname === "/index.html") return null;
|
|
46229
46310
|
return pathname + search;
|
|
46230
46311
|
}
|
|
46312
|
+
function devRpcFailure(op, id, message2, field_errors) {
|
|
46313
|
+
if (op !== "workflow") return { id, type: "error", message: message2 };
|
|
46314
|
+
var data = { status: "error", message: message2 };
|
|
46315
|
+
if (field_errors) data.field_errors = field_errors;
|
|
46316
|
+
return { id, type: "result", data };
|
|
46317
|
+
}
|
|
46231
46318
|
function buildWrapperPage(args) {
|
|
46232
46319
|
const { app_name, app_id, workspace_id, vite_url, api_url, web_app_url } = args;
|
|
46233
46320
|
return `<!doctype html>
|
|
@@ -46302,6 +46389,10 @@ function buildWrapperPage(args) {
|
|
|
46302
46389
|
appSrc.searchParams.set("lotics_host", window.location.origin);
|
|
46303
46390
|
iframe.src = appSrc.toString();
|
|
46304
46391
|
|
|
46392
|
+
// What a failed op is posted back as \u2014 resolve-vs-reject, per op, the way
|
|
46393
|
+
// both deployed transports settle it.
|
|
46394
|
+
var devRpcFailure = (${devRpcFailure.toString()});
|
|
46395
|
+
|
|
46305
46396
|
async function rpc(op, payload) {
|
|
46306
46397
|
const res = await fetch("/_rpc", {
|
|
46307
46398
|
method: "POST",
|
|
@@ -46310,9 +46401,13 @@ function buildWrapperPage(args) {
|
|
|
46310
46401
|
});
|
|
46311
46402
|
const text = await res.text();
|
|
46312
46403
|
if (!res.ok) {
|
|
46313
|
-
|
|
46314
|
-
|
|
46315
|
-
|
|
46404
|
+
// The server relays the API's own error envelope, so the refusal's
|
|
46405
|
+
// code and the inputs it named survive to the app (see rpcFailureBody).
|
|
46406
|
+
let detail = text, body = null;
|
|
46407
|
+
try { body = JSON.parse(text); detail = body.message ?? text; } catch (_) {}
|
|
46408
|
+
const err = new Error(detail || ("HTTP " + res.status));
|
|
46409
|
+
if (body) { err.code = body.code; err.field_errors = body.field_errors; }
|
|
46410
|
+
throw err;
|
|
46316
46411
|
}
|
|
46317
46412
|
return JSON.parse(text);
|
|
46318
46413
|
}
|
|
@@ -46521,9 +46616,9 @@ function buildWrapperPage(args) {
|
|
|
46521
46616
|
);
|
|
46522
46617
|
} catch (err) {
|
|
46523
46618
|
const message = err && err.message ? err.message : String(err);
|
|
46524
|
-
console.error("[lotics-dev] " + msg.op + " failed:", message);
|
|
46619
|
+
console.error("[lotics-dev] " + msg.op + " failed:", (err && err.code ? err.code + " \u2014 " : "") + message);
|
|
46525
46620
|
iframe.contentWindow.postMessage(
|
|
46526
|
-
|
|
46621
|
+
devRpcFailure(msg.op, msg.id, message, err && err.field_errors),
|
|
46527
46622
|
VITE_ORIGIN
|
|
46528
46623
|
);
|
|
46529
46624
|
}
|
|
@@ -46646,6 +46741,18 @@ var STOP_DEADLINE_MS = 2e3;
|
|
|
46646
46741
|
function serializeRpcResult(result) {
|
|
46647
46742
|
return JSON.stringify(result ?? null);
|
|
46648
46743
|
}
|
|
46744
|
+
function rpcFailureBody(err) {
|
|
46745
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
46746
|
+
if (!(err instanceof LoticsRequestError)) return { message: message2 };
|
|
46747
|
+
const code = err.body.code;
|
|
46748
|
+
const raw = err.body.field_errors;
|
|
46749
|
+
const named = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? Object.entries(raw).filter((e) => typeof e[1] === "string") : [];
|
|
46750
|
+
return {
|
|
46751
|
+
message: message2,
|
|
46752
|
+
...typeof code === "string" ? { code } : {},
|
|
46753
|
+
...named.length > 0 ? { field_errors: Object.fromEntries(named) } : {}
|
|
46754
|
+
};
|
|
46755
|
+
}
|
|
46649
46756
|
function servesWrapperPage(method, pathname) {
|
|
46650
46757
|
return method === "GET" && !pathname.startsWith("/_");
|
|
46651
46758
|
}
|
|
@@ -46772,11 +46879,11 @@ async function startDevServer(args) {
|
|
|
46772
46879
|
);
|
|
46773
46880
|
} catch (err) {
|
|
46774
46881
|
release();
|
|
46775
|
-
const
|
|
46776
|
-
process.stderr.write(`[rpc] ERROR ${
|
|
46882
|
+
const failure = rpcFailureBody(err);
|
|
46883
|
+
process.stderr.write(`[rpc] ERROR ${failure.message}
|
|
46777
46884
|
`);
|
|
46778
46885
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
46779
|
-
res.end(JSON.stringify(
|
|
46886
|
+
res.end(JSON.stringify(failure));
|
|
46780
46887
|
}
|
|
46781
46888
|
return;
|
|
46782
46889
|
}
|
|
@@ -53732,6 +53839,8 @@ var issueSchema = external_exports.object({
|
|
|
53732
53839
|
});
|
|
53733
53840
|
|
|
53734
53841
|
// src/app_commands.ts
|
|
53842
|
+
var BREAKING_API_CHANGE_CODE = "breaking_api_change";
|
|
53843
|
+
var ACKNOWLEDGMENT_ARG = "acknowledge_breaking_api_change";
|
|
53735
53844
|
function orderedLike(value, template) {
|
|
53736
53845
|
if (Array.isArray(value)) {
|
|
53737
53846
|
const t = Array.isArray(template) ? template : [];
|
|
@@ -55248,7 +55357,9 @@ async function appUpgrade(client, args) {
|
|
|
55248
55357
|
const appId = args.app_id ?? readAppMeta(process.cwd()).app_id;
|
|
55249
55358
|
let result;
|
|
55250
55359
|
try {
|
|
55251
|
-
result = await client.upgradeApp(appId
|
|
55360
|
+
result = await client.upgradeApp(appId, {
|
|
55361
|
+
acknowledge_breaking_api_change: args.acknowledgeBreakingApi
|
|
55362
|
+
});
|
|
55252
55363
|
} catch (error51) {
|
|
55253
55364
|
if (!(error51 instanceof LoticsRequestError) || error51.status !== 409 || error51.body.reason !== "already_latest") {
|
|
55254
55365
|
throw error51;
|
|
@@ -55277,6 +55388,64 @@ async function appUpgrade(client, args) {
|
|
|
55277
55388
|
}
|
|
55278
55389
|
console.error(` Edit the new code: lotics app pull ${result.app_id}`);
|
|
55279
55390
|
}
|
|
55391
|
+
function apiTargetApp(app_id) {
|
|
55392
|
+
return app_id ?? readAppMeta(process.cwd()).app_id;
|
|
55393
|
+
}
|
|
55394
|
+
async function appApiPublish(client, args) {
|
|
55395
|
+
const appId = apiTargetApp(args.app_id);
|
|
55396
|
+
let result;
|
|
55397
|
+
try {
|
|
55398
|
+
result = await client.publishAppApi(appId);
|
|
55399
|
+
} catch (error51) {
|
|
55400
|
+
if (!(error51 instanceof LoticsRequestError) || error51.status !== 400 || typeof error51.body.message !== "string") {
|
|
55401
|
+
throw error51;
|
|
55402
|
+
}
|
|
55403
|
+
fail(error51.body.message);
|
|
55404
|
+
}
|
|
55405
|
+
if (isMachineOutput()) {
|
|
55406
|
+
emitJson(result);
|
|
55407
|
+
return;
|
|
55408
|
+
}
|
|
55409
|
+
note(
|
|
55410
|
+
`Published the API of ${appId} \u2014 contract version ${result.contract_version}, ${result.published_at}.`
|
|
55411
|
+
);
|
|
55412
|
+
for (const warning of result.warnings) note(` \u26A0 ${warning}`);
|
|
55413
|
+
note(` The document its callers read: lotics app api spec ${appId}`);
|
|
55414
|
+
}
|
|
55415
|
+
async function appApiUnpublish(client, args) {
|
|
55416
|
+
const appId = apiTargetApp(args.app_id);
|
|
55417
|
+
const result = await client.unpublishAppApi(appId);
|
|
55418
|
+
if (isMachineOutput()) {
|
|
55419
|
+
emitJson(result);
|
|
55420
|
+
return;
|
|
55421
|
+
}
|
|
55422
|
+
note(
|
|
55423
|
+
result.unpublished ? `Stopped publishing the API of ${appId}.` : `${appId} was publishing no API \u2014 nothing changed.`
|
|
55424
|
+
);
|
|
55425
|
+
}
|
|
55426
|
+
async function appApiStatus(client, args) {
|
|
55427
|
+
const appId = apiTargetApp(args.app_id);
|
|
55428
|
+
const publication = await client.getAppApiPublication(appId);
|
|
55429
|
+
if (isMachineOutput()) {
|
|
55430
|
+
emitJson({ app_id: appId, ...publication });
|
|
55431
|
+
return;
|
|
55432
|
+
}
|
|
55433
|
+
console.log(
|
|
55434
|
+
publication.published ? `${appId}: publishing API contract version ${publication.contract_version}, since ${publication.published_at}.` : `${appId}: publishing no API.`
|
|
55435
|
+
);
|
|
55436
|
+
}
|
|
55437
|
+
async function appApiSpec(client, args) {
|
|
55438
|
+
const appId = apiTargetApp(args.app_id);
|
|
55439
|
+
const document2 = await client.getAppOpenApiDocument(appId);
|
|
55440
|
+
const text = `${JSON.stringify(document2, null, 2)}
|
|
55441
|
+
`;
|
|
55442
|
+
if (args.outputPath === void 0) {
|
|
55443
|
+
process.stdout.write(text);
|
|
55444
|
+
return;
|
|
55445
|
+
}
|
|
55446
|
+
fs11.writeFileSync(args.outputPath, text);
|
|
55447
|
+
note(`Wrote the OpenAPI document of ${appId} to ${args.outputPath}`);
|
|
55448
|
+
}
|
|
55280
55449
|
function defaultPullTarget(appId, appName) {
|
|
55281
55450
|
const cwdPkgPath = path12.join(process.cwd(), "package.json");
|
|
55282
55451
|
if (fs11.existsSync(cwdPkgPath)) {
|
|
@@ -55519,7 +55688,7 @@ async function appDeploy(client, args) {
|
|
|
55519
55688
|
});
|
|
55520
55689
|
let live = liveApp;
|
|
55521
55690
|
if (!nothingPending(pending)) {
|
|
55522
|
-
await pushPendingBindings(client, projectDir, pending);
|
|
55691
|
+
await pushPendingBindings(client, projectDir, pending, args.acknowledgeBreakingApi);
|
|
55523
55692
|
live = await client.getApp(meta3.app_id);
|
|
55524
55693
|
}
|
|
55525
55694
|
const sourceText = readAppSourceText(projectDir);
|
|
@@ -55594,7 +55763,8 @@ async function appDeploy(client, args) {
|
|
|
55594
55763
|
// binding.
|
|
55595
55764
|
workflow_aliases: called.workflows,
|
|
55596
55765
|
query_aliases: called.queries,
|
|
55597
|
-
agent_aliases: called.agents
|
|
55766
|
+
agent_aliases: called.agents,
|
|
55767
|
+
acknowledge_breaking_api_change: args.acknowledgeBreakingApi
|
|
55598
55768
|
});
|
|
55599
55769
|
writeAppMeta(projectDir, {
|
|
55600
55770
|
...meta3,
|
|
@@ -55651,7 +55821,8 @@ async function appDeploy(client, args) {
|
|
|
55651
55821
|
orphans,
|
|
55652
55822
|
undeclared,
|
|
55653
55823
|
called.dynamic,
|
|
55654
|
-
args.pruneInvoked ?? []
|
|
55824
|
+
args.pruneInvoked ?? [],
|
|
55825
|
+
args.acknowledgeBreakingApi
|
|
55655
55826
|
);
|
|
55656
55827
|
} else {
|
|
55657
55828
|
warnAboutOrphanedBindings(called, liveAfter, meta3.bundle_calls, undeclared);
|
|
@@ -55669,11 +55840,19 @@ async function appDeploy(client, args) {
|
|
|
55669
55840
|
}
|
|
55670
55841
|
}
|
|
55671
55842
|
} catch (err) {
|
|
55672
|
-
|
|
55673
|
-
|
|
55843
|
+
if (err instanceof LoticsRequestError && err.status === 409) {
|
|
55844
|
+
const said = typeof err.body.message === "string" ? err.body.message : err.message;
|
|
55845
|
+
if (err.body.code === BREAKING_API_CHANGE_CODE) {
|
|
55846
|
+
throw new Error(
|
|
55847
|
+
args.acknowledgeBreakingApi ? said : `${said}
|
|
55848
|
+
The flag that carries the acknowledgment here is: lotics app deploy --acknowledge-breaking-api`
|
|
55849
|
+
);
|
|
55850
|
+
}
|
|
55851
|
+
const served = "current_version_id" in err.body ? err.body.current_version_id : void 0;
|
|
55852
|
+
const stale = served !== void 0 && served !== (meta3.current_version_id ?? null);
|
|
55674
55853
|
throw new Error(
|
|
55675
|
-
|
|
55676
|
-
lotics app pull ${meta3.app_id}`
|
|
55854
|
+
stale ? `${said}
|
|
55855
|
+
Pull the latest before redeploying: lotics app pull ${meta3.app_id}` : said
|
|
55677
55856
|
);
|
|
55678
55857
|
}
|
|
55679
55858
|
throw err;
|
|
@@ -56018,7 +56197,7 @@ function reportStoredBodiesOwedMigration(projectDir, failing) {
|
|
|
56018
56197
|
Edit each body, then 'lotics app workflow set <alias>'. Until then the stored body keeps running as it always has.`
|
|
56019
56198
|
);
|
|
56020
56199
|
}
|
|
56021
|
-
async function pushPendingBindings(client, projectDir, pending) {
|
|
56200
|
+
async function pushPendingBindings(client, projectDir, pending, acknowledgeBreakingApi) {
|
|
56022
56201
|
const plan = [
|
|
56023
56202
|
...pending.queries.map((alias) => `query ${alias}`),
|
|
56024
56203
|
...pending.workflows.map((alias) => `workflow ${alias}`),
|
|
@@ -56029,10 +56208,10 @@ async function pushPendingBindings(client, projectDir, pending) {
|
|
|
56029
56208
|
Pushing ${plan.length} binding${plan.length === 1 ? "" : "s"} ahead of the app: ${plan.join(", ")}`
|
|
56030
56209
|
);
|
|
56031
56210
|
for (const alias of pending.queries) {
|
|
56032
|
-
await appQuerySet(client, { alias, projectDir });
|
|
56211
|
+
await appQuerySet(client, { alias, projectDir, acknowledgeBreakingApi });
|
|
56033
56212
|
}
|
|
56034
56213
|
for (const alias of pending.workflows) {
|
|
56035
|
-
await appWorkflowSet(client, { alias, projectDir });
|
|
56214
|
+
await appWorkflowSet(client, { alias, projectDir, acknowledgeBreakingApi });
|
|
56036
56215
|
}
|
|
56037
56216
|
for (const alias of pending.agents) {
|
|
56038
56217
|
await appAgentSet(client, { alias, projectDir });
|
|
@@ -56202,7 +56381,7 @@ function warnAboutOrphanedBindings(called, live, previouslyCalled, undeclared) {
|
|
|
56202
56381
|
);
|
|
56203
56382
|
return lines.length;
|
|
56204
56383
|
}
|
|
56205
|
-
async function pruneOrphanedBindings(client, projectDir, app, orphans, undeclared, dynamic, pruneInvoked) {
|
|
56384
|
+
async function pruneOrphanedBindings(client, projectDir, app, orphans, undeclared, dynamic, pruneInvoked, acknowledgeBreakingApi) {
|
|
56206
56385
|
const stillBound = { queries: [], workflows: [], agents: [] };
|
|
56207
56386
|
const targets = [
|
|
56208
56387
|
...orphans.queries.map((alias) => ({
|
|
@@ -56260,7 +56439,9 @@ async function pruneOrphanedBindings(client, projectDir, app, orphans, undeclare
|
|
|
56260
56439
|
// Sent only for the aliases the operator named, so the default stays the
|
|
56261
56440
|
// guard: a run recorded against an alias means something outside the
|
|
56262
56441
|
// bundle reaches it.
|
|
56263
|
-
...overrideInvocationGuard ? { even_if_invoked: true } : {}
|
|
56442
|
+
...overrideInvocationGuard ? { even_if_invoked: true } : {},
|
|
56443
|
+
// Agents carry no published entry, so the field is not theirs to take.
|
|
56444
|
+
...acknowledgeBreakingApi && target.kind !== "agents" ? { acknowledge_breaking_api_change: true } : {}
|
|
56264
56445
|
});
|
|
56265
56446
|
if (res.error) {
|
|
56266
56447
|
console.error(` \u2717 could not unbind ${target.label} \u2014 ${res.error}`);
|
|
@@ -56889,6 +57070,13 @@ function renderWorkflowIssue(issue2) {
|
|
|
56889
57070
|
return issue2.suggestion ? `${head}
|
|
56890
57071
|
${issue2.suggestion}` : head;
|
|
56891
57072
|
}
|
|
57073
|
+
function refusalAtThisSurface(res, command, acknowledged) {
|
|
57074
|
+
const said = res.error ?? "";
|
|
57075
|
+
const breaking = res.error_code === void 0 ? said.includes(ACKNOWLEDGMENT_ARG) : res.error_code === BREAKING_API_CHANGE_CODE;
|
|
57076
|
+
if (acknowledged === true || !breaking) return said;
|
|
57077
|
+
return `${said}
|
|
57078
|
+
The flag that carries the acknowledgment here is: ${command} --acknowledge-breaking-api`;
|
|
57079
|
+
}
|
|
56892
57080
|
async function appWorkflowSet(client, args) {
|
|
56893
57081
|
const projectDir = args.projectDir ?? process.cwd();
|
|
56894
57082
|
const meta3 = readAppMeta(projectDir);
|
|
@@ -56930,10 +57118,19 @@ async function appWorkflowSet(client, args) {
|
|
|
56930
57118
|
// The body this edit was built on. Turns the push into a conditional write:
|
|
56931
57119
|
// if the live body moved since this project last saw it, the server refuses
|
|
56932
57120
|
// rather than letting this copy overwrite a change nobody here has seen.
|
|
56933
|
-
expected_body_sha: readSynced(projectDir).workflows[args.alias]?.live
|
|
57121
|
+
expected_body_sha: readSynced(projectDir).workflows[args.alias]?.live,
|
|
57122
|
+
// The author's answer to a published contract this body would break. Absent,
|
|
57123
|
+
// the server refuses such a write and names each change.
|
|
57124
|
+
acknowledge_breaking_api_change: args.acknowledgeBreakingApi
|
|
56934
57125
|
});
|
|
56935
57126
|
if (res.error) {
|
|
56936
|
-
console.error(
|
|
57127
|
+
console.error(
|
|
57128
|
+
`Failed to set workflow "${args.alias}": ` + refusalAtThisSurface(
|
|
57129
|
+
res,
|
|
57130
|
+
`lotics app workflow set ${args.alias}`,
|
|
57131
|
+
args.acknowledgeBreakingApi
|
|
57132
|
+
)
|
|
57133
|
+
);
|
|
56937
57134
|
process.exit(1);
|
|
56938
57135
|
}
|
|
56939
57136
|
const parsed = zod_default.object({
|
|
@@ -57007,11 +57204,16 @@ async function appQuerySet(client, args) {
|
|
|
57007
57204
|
meta3.app_id,
|
|
57008
57205
|
alias,
|
|
57009
57206
|
declaration,
|
|
57010
|
-
readSynced(projectDir).queries[alias]?.live
|
|
57207
|
+
readSynced(projectDir).queries[alias]?.live,
|
|
57208
|
+
// The author's answer to a published contract this declaration would
|
|
57209
|
+
// break. Absent, the server refuses such a write and names each change.
|
|
57210
|
+
args.acknowledgeBreakingApi
|
|
57011
57211
|
);
|
|
57012
57212
|
if (res.error) {
|
|
57013
57213
|
if (pushed.length > 0) console.error(`Pushed before the failure: ${pushed.join(", ")}.`);
|
|
57014
|
-
console.error(
|
|
57214
|
+
console.error(
|
|
57215
|
+
`Failed to set query "${alias}": ` + refusalAtThisSurface(res, `lotics app query set ${alias}`, args.acknowledgeBreakingApi)
|
|
57216
|
+
);
|
|
57015
57217
|
process.exit(1);
|
|
57016
57218
|
}
|
|
57017
57219
|
const querySha = declarationSha(declaration);
|
|
@@ -57405,6 +57607,7 @@ function parseArgs(argv) {
|
|
|
57405
57607
|
cleanup: false,
|
|
57406
57608
|
prune: false,
|
|
57407
57609
|
pruneInvoked: [],
|
|
57610
|
+
acknowledgeBreakingApi: false,
|
|
57408
57611
|
screens: false,
|
|
57409
57612
|
screen: [],
|
|
57410
57613
|
width: [],
|
|
@@ -57582,6 +57785,9 @@ function parseArgs(argv) {
|
|
|
57582
57785
|
case "--prune":
|
|
57583
57786
|
flags.prune = true;
|
|
57584
57787
|
break;
|
|
57788
|
+
case "--acknowledge-breaking-api":
|
|
57789
|
+
flags.acknowledgeBreakingApi = true;
|
|
57790
|
+
break;
|
|
57585
57791
|
case "--screens":
|
|
57586
57792
|
flags.screens = true;
|
|
57587
57793
|
break;
|
|
@@ -57691,6 +57897,8 @@ function saveApproval(state, pending, options) {
|
|
|
57691
57897
|
requireNonEmpty(state.workspace_id, "workspace_id");
|
|
57692
57898
|
upsertProfile(state.organization_id, {
|
|
57693
57899
|
api_key: state.api_key,
|
|
57900
|
+
// A person pressed Confirm: this row IS their terminal's session.
|
|
57901
|
+
kind: "sign_in",
|
|
57694
57902
|
org_name: state.organization_name,
|
|
57695
57903
|
// The host that minted the key, taken from the request rather than re-read:
|
|
57696
57904
|
// this is the whole of "which Lotics does this registration belong to".
|
|
@@ -58370,7 +58578,7 @@ async function signupAccount(args) {
|
|
|
58370
58578
|
const orgId = data.organization_id;
|
|
58371
58579
|
const workspaceId = data.workspace_id;
|
|
58372
58580
|
const orgName = args.name || args.email.split("@")[0];
|
|
58373
|
-
upsertProfile(orgId, { api_key: apiKey, org_name: orgName, api_url: apiUrl, workspace_id: workspaceId });
|
|
58581
|
+
upsertProfile(orgId, { api_key: apiKey, kind: "sign_in", org_name: orgName, api_url: apiUrl, workspace_id: workspaceId });
|
|
58374
58582
|
const existing = loadGlobalConfig() ?? {};
|
|
58375
58583
|
saveGlobalConfig({ ...existing, email: args.email });
|
|
58376
58584
|
setActiveOrg(orgId, args.local ? "local" : "global");
|
|
@@ -58540,24 +58748,54 @@ Multiple workspaces in ${info.organization_name}. Run "lotics workspace select <
|
|
|
58540
58748
|
}
|
|
58541
58749
|
upsertProfile(info.organization_id, {
|
|
58542
58750
|
api_key: apiKey,
|
|
58751
|
+
// Pasted in rather than signed in: nobody signs in as an API key, and
|
|
58752
|
+
// `auth whoami` says which this machine is holding.
|
|
58753
|
+
kind: "api_key",
|
|
58543
58754
|
org_name: info.organization_name,
|
|
58544
58755
|
api_url: apiUrl,
|
|
58545
58756
|
workspace_id: workspaceId,
|
|
58546
58757
|
workspace_name: workspaceName
|
|
58547
58758
|
});
|
|
58548
58759
|
noteServerOrgName(info.organization_id, info.organization_name);
|
|
58549
|
-
|
|
58550
|
-
|
|
58760
|
+
if (info.email !== null) {
|
|
58761
|
+
saveGlobalConfig({ ...loadGlobalConfig(), email: info.email });
|
|
58762
|
+
}
|
|
58551
58763
|
setActiveOrg(info.organization_id, local ? "local" : "global");
|
|
58552
58764
|
clearPendingLogin();
|
|
58765
|
+
const who = info.email ?? info.name;
|
|
58553
58766
|
if (local) {
|
|
58554
|
-
console.error(`Authenticated as ${
|
|
58767
|
+
console.error(`Authenticated as ${who} in ${info.organization_name}.`);
|
|
58555
58768
|
console.error(` Pinned this directory: ${getConfigPath("local")}`);
|
|
58556
58769
|
} else {
|
|
58557
|
-
console.error(`Authenticated as ${
|
|
58770
|
+
console.error(`Authenticated as ${who} in ${info.organization_name} (now active).`);
|
|
58558
58771
|
console.error(` Saved to the "${info.organization_name}" profile in ~/.lotics`);
|
|
58559
58772
|
}
|
|
58560
58773
|
}
|
|
58774
|
+
async function credentialIsSignIn(client, profile) {
|
|
58775
|
+
if (profile.kind) return profile.kind === "sign_in";
|
|
58776
|
+
try {
|
|
58777
|
+
return (await client.whoami()).credential_origin === "sign_in";
|
|
58778
|
+
} catch {
|
|
58779
|
+
return false;
|
|
58780
|
+
}
|
|
58781
|
+
}
|
|
58782
|
+
async function revokeCredential(orgId, profile) {
|
|
58783
|
+
const client = new LoticsClient({ apiKey: profile.api_key, apiUrl: profile.api_url });
|
|
58784
|
+
if (!await credentialIsSignIn(client, profile)) {
|
|
58785
|
+
console.error(
|
|
58786
|
+
`The "${profile.org_name}" key (${orgId}) was removed from this machine and is still active \u2014 revoke it yourself under Settings \u2192 Security \u2192 "Keys and terminals" if it acts as you, or ask an admin (Settings \u2192 API keys).`
|
|
58787
|
+
);
|
|
58788
|
+
return;
|
|
58789
|
+
}
|
|
58790
|
+
try {
|
|
58791
|
+
await client.revokeSelfCredential();
|
|
58792
|
+
} catch (error51) {
|
|
58793
|
+
const reason = (error51 instanceof Error ? error51.message : String(error51)).replace(/\s+/g, " ");
|
|
58794
|
+
console.error(
|
|
58795
|
+
`Could not revoke the "${profile.org_name}" credential (${orgId}) server-side (${reason}) \u2014 it is forgotten here, so revoke it under Settings \u2192 Security \u2192 "Keys and terminals" if it is still live.`
|
|
58796
|
+
);
|
|
58797
|
+
}
|
|
58798
|
+
}
|
|
58561
58799
|
async function resolveContextOrClaim(flags, options) {
|
|
58562
58800
|
const appWorkspaceId = options?.appWorkspaceId;
|
|
58563
58801
|
const resolved = resolveContext(flags, appWorkspaceId);
|
|
@@ -59205,7 +59443,7 @@ async function main() {
|
|
|
59205
59443
|
const client2 = new LoticsClient({ apiKey: ctx2.apiKey, apiUrl: ctx2.apiUrl });
|
|
59206
59444
|
const info = await client2.whoami();
|
|
59207
59445
|
const existing = loadGlobalConfig() ?? {};
|
|
59208
|
-
saveGlobalConfig({ ...existing, email: info.email });
|
|
59446
|
+
if (info.email !== null) saveGlobalConfig({ ...existing, email: info.email });
|
|
59209
59447
|
const cached2 = existing.profiles?.[info.organization_id];
|
|
59210
59448
|
if (cached2 && (cached2.server_name ?? cached2.org_name) !== info.organization_name) {
|
|
59211
59449
|
noteServerOrgName(info.organization_id, info.organization_name);
|
|
@@ -59216,14 +59454,17 @@ async function main() {
|
|
|
59216
59454
|
}
|
|
59217
59455
|
}
|
|
59218
59456
|
const viewAs = flags.viewAs ?? process.env.LOTICS_VIEW_AS;
|
|
59457
|
+
const kind = cached2?.kind ?? info.credential_origin ?? void 0;
|
|
59458
|
+
const kindLabel = kind === "sign_in" ? "sign-in (lotics auth login)" : kind === "api_key" ? "API key" : "unknown";
|
|
59219
59459
|
if (flags.json) {
|
|
59220
|
-
console.log(JSON.stringify({ ...info, workspace_id: ctx2.workspaceId ?? null, api_url: ctx2.apiUrl, source: ctx2.source, view_as: viewAs ?? null }, null, 2));
|
|
59460
|
+
console.log(JSON.stringify({ ...info, workspace_id: ctx2.workspaceId ?? null, api_url: ctx2.apiUrl, source: ctx2.source, credential_kind: kind ?? null, view_as: viewAs ?? null }, null, 2));
|
|
59221
59461
|
} else {
|
|
59222
59462
|
console.log(`Name: ${info.name}`);
|
|
59223
|
-
console.log(`Email: ${info.email}`);
|
|
59463
|
+
console.log(`Email: ${info.email ?? "(none: nobody signs in as this key)"}`);
|
|
59224
59464
|
console.log(`Org: ${info.organization_name} (${info.organization_id})`);
|
|
59225
59465
|
console.log(`Workspace: ${ctx2.workspaceId ?? "(none selected)"}`);
|
|
59226
59466
|
console.log(`API: ${ctx2.apiUrl}`);
|
|
59467
|
+
console.log(`Credential: ${kindLabel}`);
|
|
59227
59468
|
console.log(`Source: ${SOURCE_LABELS[ctx2.source]}`);
|
|
59228
59469
|
if (viewAs) console.log(`View as: ${viewAs} (admin preview; requires an admin key)`);
|
|
59229
59470
|
}
|
|
@@ -59238,6 +59479,9 @@ async function main() {
|
|
|
59238
59479
|
}
|
|
59239
59480
|
if (flags.all) {
|
|
59240
59481
|
const g2 = loadGlobalConfig();
|
|
59482
|
+
for (const [orgId2, profile2] of Object.entries(g2?.profiles ?? {})) {
|
|
59483
|
+
await revokeCredential(orgId2, profile2);
|
|
59484
|
+
}
|
|
59241
59485
|
saveGlobalConfig({ last_update_check: g2?.last_update_check, latest_version: g2?.latest_version });
|
|
59242
59486
|
console.error("Removed all saved credentials.");
|
|
59243
59487
|
return;
|
|
@@ -59257,6 +59501,7 @@ async function main() {
|
|
|
59257
59501
|
process.exit(1);
|
|
59258
59502
|
}
|
|
59259
59503
|
const [orgId, profile] = resolved;
|
|
59504
|
+
await revokeCredential(orgId, profile);
|
|
59260
59505
|
removeProfile(orgId);
|
|
59261
59506
|
clearPendingLogin();
|
|
59262
59507
|
console.error(`Removed the "${profile.org_name}" credential (${orgId}).`);
|
|
@@ -59417,6 +59662,9 @@ async function main() {
|
|
|
59417
59662
|
console.error(" lotics app workflow pull Rewrite src/workflows/*.ts from the server");
|
|
59418
59663
|
console.error(" lotics app workflow check [alias] Typecheck src/workflows bodies locally");
|
|
59419
59664
|
console.error(" lotics app query set <alias> Push lotics.queries.<alias> to apps.queries (no deploy)");
|
|
59665
|
+
console.error(" lotics app api publish|unpublish|status|spec [app_id]");
|
|
59666
|
+
console.error(" The app's API: publish the promise its queries and");
|
|
59667
|
+
console.error(" workflows make to callers outside it, or read it back");
|
|
59420
59668
|
console.error(" lotics app subdomain <new-subdomain> Rename the app's public address (its subdomain)");
|
|
59421
59669
|
console.error(` lotics app rename "<new name>" Rename the app's display name (launcher title)`);
|
|
59422
59670
|
console.error(" lotics app dev [path] Run the app locally with HMR + RPC forwarding");
|
|
@@ -59738,10 +59986,40 @@ Available workspaces:`);
|
|
|
59738
59986
|
await appDeploy(client, {
|
|
59739
59987
|
message: (flags.message ?? toolArgs)?.trim() || void 0,
|
|
59740
59988
|
prune: flags.prune,
|
|
59741
|
-
pruneInvoked: flags.pruneInvoked
|
|
59989
|
+
pruneInvoked: flags.pruneInvoked,
|
|
59990
|
+
acknowledgeBreakingApi: flags.acknowledgeBreakingApi
|
|
59742
59991
|
});
|
|
59743
59992
|
return;
|
|
59744
59993
|
}
|
|
59994
|
+
if (subcommand === "api") {
|
|
59995
|
+
const action = toolArgs;
|
|
59996
|
+
const appId = restArgs.find((a) => !a.startsWith("-"));
|
|
59997
|
+
if (flags.json) setMachineOutput(true);
|
|
59998
|
+
if (action === "publish") {
|
|
59999
|
+
await appApiPublish(client, { app_id: appId });
|
|
60000
|
+
return;
|
|
60001
|
+
}
|
|
60002
|
+
if (action === "unpublish") {
|
|
60003
|
+
await appApiUnpublish(client, { app_id: appId });
|
|
60004
|
+
return;
|
|
60005
|
+
}
|
|
60006
|
+
if (action === "status") {
|
|
60007
|
+
await appApiStatus(client, { app_id: appId });
|
|
60008
|
+
return;
|
|
60009
|
+
}
|
|
60010
|
+
if (action === "spec") {
|
|
60011
|
+
await appApiSpec(client, { app_id: appId, outputPath: flags.output });
|
|
60012
|
+
return;
|
|
60013
|
+
}
|
|
60014
|
+
console.error("Usage:");
|
|
60015
|
+
console.error(" lotics app api publish [app_id] Publish the app's API and snapshot its contract");
|
|
60016
|
+
console.error(" lotics app api unpublish [app_id] Stop publishing it");
|
|
60017
|
+
console.error(" lotics app api status [app_id] Whether it publishes one, and which version");
|
|
60018
|
+
console.error(" lotics app api spec [app_id] [-o <file.json>]");
|
|
60019
|
+
console.error(" The OpenAPI document its callers read");
|
|
60020
|
+
console.error(" --json One object on stdout, and nothing else");
|
|
60021
|
+
process.exit(1);
|
|
60022
|
+
}
|
|
59745
60023
|
if (subcommand === "subdomain") {
|
|
59746
60024
|
const newSubdomain = toolArgs;
|
|
59747
60025
|
if (!newSubdomain) {
|
|
@@ -59768,7 +60046,10 @@ Available workspaces:`);
|
|
|
59768
60046
|
return;
|
|
59769
60047
|
}
|
|
59770
60048
|
if (subcommand === "upgrade") {
|
|
59771
|
-
await appUpgrade(client, {
|
|
60049
|
+
await appUpgrade(client, {
|
|
60050
|
+
app_id: toolArgs || void 0,
|
|
60051
|
+
acknowledgeBreakingApi: flags.acknowledgeBreakingApi
|
|
60052
|
+
});
|
|
59772
60053
|
return;
|
|
59773
60054
|
}
|
|
59774
60055
|
if (subcommand === "check") {
|
|
@@ -59808,7 +60089,10 @@ Available workspaces:`);
|
|
|
59808
60089
|
console.error("Pushes the edited src/workflows/<alias>.ts body via set_app_workflow.");
|
|
59809
60090
|
process.exit(1);
|
|
59810
60091
|
}
|
|
59811
|
-
await appWorkflowSet(client, {
|
|
60092
|
+
await appWorkflowSet(client, {
|
|
60093
|
+
alias,
|
|
60094
|
+
acknowledgeBreakingApi: flags.acknowledgeBreakingApi
|
|
60095
|
+
});
|
|
59812
60096
|
return;
|
|
59813
60097
|
}
|
|
59814
60098
|
if (action === "pull") {
|
|
@@ -59844,7 +60128,10 @@ Available workspaces:`);
|
|
|
59844
60128
|
const action = toolArgs;
|
|
59845
60129
|
if (action === "set") {
|
|
59846
60130
|
if (flags.all) {
|
|
59847
|
-
await appQuerySet(client, {
|
|
60131
|
+
await appQuerySet(client, {
|
|
60132
|
+
all: true,
|
|
60133
|
+
acknowledgeBreakingApi: flags.acknowledgeBreakingApi
|
|
60134
|
+
});
|
|
59848
60135
|
return;
|
|
59849
60136
|
}
|
|
59850
60137
|
const alias = restArgs.find((a) => !a.startsWith("--"));
|
|
@@ -59855,7 +60142,7 @@ Available workspaces:`);
|
|
|
59855
60142
|
);
|
|
59856
60143
|
process.exit(1);
|
|
59857
60144
|
}
|
|
59858
|
-
await appQuerySet(client, { alias });
|
|
60145
|
+
await appQuerySet(client, { alias, acknowledgeBreakingApi: flags.acknowledgeBreakingApi });
|
|
59859
60146
|
return;
|
|
59860
60147
|
}
|
|
59861
60148
|
console.error("Usage:");
|