@lotics/cli 0.88.0 → 0.89.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/dist/render_page.js +100 -100
- package/dist/src/cli.js +162 -100
- package/dist/{client.d.ts → src/client.d.ts} +9 -1
- package/dist/src/package_content_types.d.ts +70 -0
- package/dist/src/package_content_types.js +12 -0
- package/package.json +2 -2
- package/dist/app_commands.d.ts +0 -401
- package/dist/app_commands.js +0 -1520
- package/dist/app_commands.test.d.ts +0 -1
- package/dist/app_commands.test.js +0 -898
- package/dist/app_workflow_check.d.ts +0 -77
- package/dist/app_workflow_check.js +0 -169
- package/dist/app_workflow_check.test.d.ts +0 -1
- package/dist/app_workflow_check.test.js +0 -166
- package/dist/args.d.ts +0 -42
- package/dist/args.js +0 -121
- package/dist/args.test.d.ts +0 -1
- package/dist/args.test.js +0 -88
- package/dist/child_env.d.ts +0 -13
- package/dist/child_env.js +0 -24
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -1095
- package/dist/cli_dispatch.test.d.ts +0 -1
- package/dist/cli_dispatch.test.js +0 -89
- package/dist/client.test.d.ts +0 -1
- package/dist/client.test.js +0 -47
- package/dist/config.d.ts +0 -124
- package/dist/config.js +0 -315
- package/dist/config.test.d.ts +0 -1
- package/dist/config.test.js +0 -319
- package/dist/dev/file_relay.d.ts +0 -39
- package/dist/dev/file_relay.js +0 -87
- package/dist/dev/file_relay.test.d.ts +0 -1
- package/dist/dev/file_relay.test.js +0 -87
- package/dist/dev/rpc_handler.d.ts +0 -21
- package/dist/dev/rpc_handler.js +0 -166
- package/dist/dev/rpc_handler.test.d.ts +0 -1
- package/dist/dev/rpc_handler.test.js +0 -107
- package/dist/dev/server.d.ts +0 -75
- package/dist/dev/server.js +0 -443
- package/dist/dev/upload_relay.d.ts +0 -36
- package/dist/dev/upload_relay.js +0 -61
- package/dist/dev/upload_relay.test.d.ts +0 -1
- package/dist/dev/upload_relay.test.js +0 -76
- package/dist/dev/wrapper_page.d.ts +0 -40
- package/dist/dev/wrapper_page.js +0 -335
- package/dist/docx.d.ts +0 -2
- package/dist/docx.js +0 -341
- package/dist/docx.test.d.ts +0 -1
- package/dist/docx.test.js +0 -145
- package/dist/file_command_io.d.ts +0 -12
- package/dist/file_command_io.js +0 -34
- package/dist/generate_app_agents_dts.d.ts +0 -16
- package/dist/generate_app_agents_dts.js +0 -62
- package/dist/generate_app_agents_dts.test.d.ts +0 -1
- package/dist/generate_app_agents_dts.test.js +0 -45
- package/dist/generate_app_fields.d.ts +0 -56
- package/dist/generate_app_fields.js +0 -148
- package/dist/generate_app_fields.test.d.ts +0 -1
- package/dist/generate_app_fields.test.js +0 -108
- package/dist/generate_app_queries_dts.d.ts +0 -21
- package/dist/generate_app_queries_dts.js +0 -57
- package/dist/generate_app_workflows_dts.d.ts +0 -39
- package/dist/generate_app_workflows_dts.js +0 -229
- package/dist/generate_app_workflows_dts.test.d.ts +0 -1
- package/dist/generate_app_workflows_dts.test.js +0 -109
- package/dist/generate_package_fields.d.ts +0 -49
- package/dist/generate_package_fields.js +0 -157
- package/dist/generate_package_fields.test.d.ts +0 -1
- package/dist/generate_package_fields.test.js +0 -59
- package/dist/inputs.d.ts +0 -38
- package/dist/inputs.js +0 -50
- package/dist/inputs.test.d.ts +0 -1
- package/dist/inputs.test.js +0 -89
- package/dist/preview.d.ts +0 -3
- package/dist/preview.js +0 -233
- package/dist/starter_template.d.ts +0 -64
- package/dist/starter_template.js +0 -753
- package/dist/starter_template.test.d.ts +0 -1
- package/dist/starter_template.test.js +0 -115
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -6
- package/dist/xlsx.d.ts +0 -2
- package/dist/xlsx.js +0 -489
- package/dist/xlsx.test.d.ts +0 -1
- package/dist/xlsx.test.js +0 -131
- /package/dist/{client.js → src/client.js} +0 -0
package/dist/dev/rpc_handler.js
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stateless dispatcher: takes a parsed RPC envelope from the wrapper page
|
|
3
|
-
* and routes to the matching LoticsClient method.
|
|
4
|
-
*
|
|
5
|
-
* Errors are thrown — the HTTP server caller serializes them to a 500 with
|
|
6
|
-
* { message }. Same shape as the production iframe-host's error path.
|
|
7
|
-
*/
|
|
8
|
-
const SUPPORTED_OPS = new Set([
|
|
9
|
-
"query",
|
|
10
|
-
"field_options",
|
|
11
|
-
"workflow",
|
|
12
|
-
"members",
|
|
13
|
-
"context",
|
|
14
|
-
"binding",
|
|
15
|
-
"upload_url",
|
|
16
|
-
"upload_complete",
|
|
17
|
-
"comments.list",
|
|
18
|
-
"comments.create",
|
|
19
|
-
"comments.update",
|
|
20
|
-
"comments.delete",
|
|
21
|
-
"comments.counts",
|
|
22
|
-
]);
|
|
23
|
-
export async function dispatchRpc(client, body, opts) {
|
|
24
|
-
if (!body || typeof body.app_id !== "string" || typeof body.op !== "string") {
|
|
25
|
-
throw new Error("RPC envelope must include app_id and op");
|
|
26
|
-
}
|
|
27
|
-
if (!SUPPORTED_OPS.has(body.op)) {
|
|
28
|
-
throw new Error(`Unknown RPC op: ${body.op}`);
|
|
29
|
-
}
|
|
30
|
-
switch (body.op) {
|
|
31
|
-
case "context": {
|
|
32
|
-
// In production the iframe host supplies the context; here we resolve the
|
|
33
|
-
// effective viewer from the CLI key (the view-as target when the client
|
|
34
|
-
// carries `x-view-as-member-id`, else the key's owner), so `useViewer`
|
|
35
|
-
// and `is_current_member` agree in the dev loop. The installation's
|
|
36
|
-
// stored config is fetched live for the same parity: `useConfig()` must
|
|
37
|
-
// render the same values in the dev loop as in production, including
|
|
38
|
-
// edits made in the product mid-session (fresh on every app load, no
|
|
39
|
-
// dev-server restart).
|
|
40
|
-
const [who, app] = await Promise.all([client.whoami(), client.getApp(body.app_id)]);
|
|
41
|
-
return {
|
|
42
|
-
member_id: who.member_id,
|
|
43
|
-
comments_enabled: opts?.commentsEnabled ?? false,
|
|
44
|
-
config: app.config ?? {},
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
case "query": {
|
|
48
|
-
const p = body.payload;
|
|
49
|
-
if (!p || typeof p.alias !== "string") {
|
|
50
|
-
throw new Error("query payload must include `alias`");
|
|
51
|
-
}
|
|
52
|
-
return client.appQuery(body.app_id, {
|
|
53
|
-
alias: p.alias,
|
|
54
|
-
params: p.params,
|
|
55
|
-
limit: p.limit,
|
|
56
|
-
offset: p.offset,
|
|
57
|
-
sort: p.sort,
|
|
58
|
-
filter: p.filter,
|
|
59
|
-
count: p.count,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
case "field_options": {
|
|
63
|
-
const p = body.payload;
|
|
64
|
-
if (!p || typeof p.alias !== "string") {
|
|
65
|
-
throw new Error("field_options payload must include `alias`");
|
|
66
|
-
}
|
|
67
|
-
return client.appFieldOptions(body.app_id, p.alias);
|
|
68
|
-
}
|
|
69
|
-
case "workflow": {
|
|
70
|
-
const p = body.payload;
|
|
71
|
-
if (!p || typeof p.alias !== "string") {
|
|
72
|
-
throw new Error("workflow payload must include `alias`");
|
|
73
|
-
}
|
|
74
|
-
return client.appWorkflow(body.app_id, p.alias, p.inputs);
|
|
75
|
-
}
|
|
76
|
-
case "members": {
|
|
77
|
-
const p = body.payload;
|
|
78
|
-
return client.appMembers(body.app_id, p?.group);
|
|
79
|
-
}
|
|
80
|
-
case "binding":
|
|
81
|
-
return client.appBinding(body.app_id);
|
|
82
|
-
case "upload_url": {
|
|
83
|
-
const p = body.payload;
|
|
84
|
-
if (!p ||
|
|
85
|
-
typeof p.filename !== "string" ||
|
|
86
|
-
typeof p.mime_type !== "string" ||
|
|
87
|
-
typeof p.file_size !== "number") {
|
|
88
|
-
throw new Error("upload_url payload must include filename, mime_type, file_size");
|
|
89
|
-
}
|
|
90
|
-
return client.appRequestFileUpload(body.app_id, {
|
|
91
|
-
filename: p.filename,
|
|
92
|
-
mime_type: p.mime_type,
|
|
93
|
-
file_size: p.file_size,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
case "upload_complete": {
|
|
97
|
-
const p = body.payload;
|
|
98
|
-
if (!p ||
|
|
99
|
-
typeof p.file_id !== "string" ||
|
|
100
|
-
typeof p.file_storage_key !== "string" ||
|
|
101
|
-
typeof p.filename !== "string") {
|
|
102
|
-
throw new Error("upload_complete payload must include file_id, file_storage_key, filename");
|
|
103
|
-
}
|
|
104
|
-
return client.appCompleteFileUpload(body.app_id, {
|
|
105
|
-
file_id: p.file_id,
|
|
106
|
-
file_storage_key: p.file_storage_key,
|
|
107
|
-
filename: p.filename,
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
// Comment ops mirror the production iframe-host's `handleCommentRpc` return
|
|
111
|
-
// shapes so the SDK hooks behave identically in dev. App authority + the
|
|
112
|
-
// `comments` capability + tenant floor are enforced server-side.
|
|
113
|
-
case "comments.list": {
|
|
114
|
-
const p = body.payload;
|
|
115
|
-
if (!p || typeof p.record_id !== "string") {
|
|
116
|
-
throw new Error("comments.list payload must include record_id");
|
|
117
|
-
}
|
|
118
|
-
const comments = await client.appGetRecordComments(body.app_id, p.record_id);
|
|
119
|
-
return { comments };
|
|
120
|
-
}
|
|
121
|
-
case "comments.create": {
|
|
122
|
-
const p = body.payload;
|
|
123
|
-
if (!p || typeof p.record_id !== "string" || typeof p.content !== "string") {
|
|
124
|
-
throw new Error("comments.create payload must include record_id and content");
|
|
125
|
-
}
|
|
126
|
-
const created = await client.appCreateRecordComment(body.app_id, p.record_id, {
|
|
127
|
-
content: p.content,
|
|
128
|
-
file_ids: p.file_ids,
|
|
129
|
-
});
|
|
130
|
-
return { comments: created ? [created] : [] };
|
|
131
|
-
}
|
|
132
|
-
case "comments.update": {
|
|
133
|
-
const p = body.payload;
|
|
134
|
-
if (!p ||
|
|
135
|
-
typeof p.record_id !== "string" ||
|
|
136
|
-
typeof p.comment_id !== "string" ||
|
|
137
|
-
typeof p.content !== "string") {
|
|
138
|
-
throw new Error("comments.update payload must include record_id, comment_id, content");
|
|
139
|
-
}
|
|
140
|
-
const comment = await client.appUpdateRecordComment(body.app_id, p.record_id, p.comment_id, {
|
|
141
|
-
content: p.content,
|
|
142
|
-
files: p.files,
|
|
143
|
-
});
|
|
144
|
-
return { comment };
|
|
145
|
-
}
|
|
146
|
-
case "comments.delete": {
|
|
147
|
-
const p = body.payload;
|
|
148
|
-
if (!p || typeof p.record_id !== "string" || typeof p.comment_id !== "string") {
|
|
149
|
-
throw new Error("comments.delete payload must include record_id and comment_id");
|
|
150
|
-
}
|
|
151
|
-
await client.appDeleteRecordComment(body.app_id, p.record_id, p.comment_id);
|
|
152
|
-
return undefined;
|
|
153
|
-
}
|
|
154
|
-
case "comments.counts": {
|
|
155
|
-
const p = body.payload;
|
|
156
|
-
if (!p || typeof p.table_id !== "string") {
|
|
157
|
-
throw new Error("comments.counts payload must include table_id");
|
|
158
|
-
}
|
|
159
|
-
return client.appGetTableCommentCounts(body.app_id, p.table_id);
|
|
160
|
-
}
|
|
161
|
-
default: {
|
|
162
|
-
// Unreachable — SUPPORTED_OPS gates above.
|
|
163
|
-
throw new Error(`Unhandled RPC op: ${body.op}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { dispatchRpc } from "./rpc_handler.js";
|
|
3
|
-
import { serializeRpcResult } from "./server.js";
|
|
4
|
-
function mockClient(over) {
|
|
5
|
-
return over;
|
|
6
|
-
}
|
|
7
|
-
const WHOAMI = {
|
|
8
|
-
member_id: "mbr_viewer",
|
|
9
|
-
email: "v@test.local",
|
|
10
|
-
name: "Viewer",
|
|
11
|
-
organization_id: "org_1",
|
|
12
|
-
organization_name: "Org",
|
|
13
|
-
};
|
|
14
|
-
describe("dispatchRpc — context op", () => {
|
|
15
|
-
it("resolves the viewer from whoami and the installation's stored config from the live app row", async () => {
|
|
16
|
-
const client = mockClient({
|
|
17
|
-
whoami: async () => WHOAMI,
|
|
18
|
-
getApp: async () => ({
|
|
19
|
-
id: "app_x",
|
|
20
|
-
name: "App X",
|
|
21
|
-
workspace_id: "wsp_1",
|
|
22
|
-
current_version_id: null,
|
|
23
|
-
config: { page_size: 50 },
|
|
24
|
-
}),
|
|
25
|
-
});
|
|
26
|
-
const result = await dispatchRpc(client, { app_id: "app_x", op: "context", payload: {} }, { commentsEnabled: true });
|
|
27
|
-
expect(result).toEqual({ member_id: "mbr_viewer", comments_enabled: true, config: { page_size: 50 } });
|
|
28
|
-
});
|
|
29
|
-
it("defaults comments_enabled to false and config to empty for a bespoke app", async () => {
|
|
30
|
-
const client = mockClient({
|
|
31
|
-
whoami: async () => WHOAMI,
|
|
32
|
-
getApp: async () => ({
|
|
33
|
-
id: "app_x",
|
|
34
|
-
name: "App X",
|
|
35
|
-
workspace_id: "wsp_1",
|
|
36
|
-
current_version_id: null,
|
|
37
|
-
config: null,
|
|
38
|
-
}),
|
|
39
|
-
});
|
|
40
|
-
const result = await dispatchRpc(client, { app_id: "app_x", op: "context", payload: {} });
|
|
41
|
-
expect(result).toEqual({ member_id: "mbr_viewer", comments_enabled: false, config: {} });
|
|
42
|
-
});
|
|
43
|
-
it("rejects an op that isn't supported", async () => {
|
|
44
|
-
const client = mockClient({});
|
|
45
|
-
await expect(dispatchRpc(client, { app_id: "app_x", op: "bogus", payload: {} })).rejects.toThrow(/Unknown RPC op/);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
describe("dispatchRpc — field_options op", () => {
|
|
49
|
-
it("forwards the alias to appFieldOptions and returns its { fields } shape", async () => {
|
|
50
|
-
const fields = {
|
|
51
|
-
status: {
|
|
52
|
-
label: "Status",
|
|
53
|
-
options: [{ key: "opt_open", label: "Open", color: "blue" }],
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
const client = mockClient({ appFieldOptions: async () => ({ fields }) });
|
|
57
|
-
const result = await dispatchRpc(client, {
|
|
58
|
-
app_id: "app_x",
|
|
59
|
-
op: "field_options",
|
|
60
|
-
payload: { alias: "records" },
|
|
61
|
-
});
|
|
62
|
-
expect(result).toEqual({ fields });
|
|
63
|
-
});
|
|
64
|
-
it("requires an alias in the payload", async () => {
|
|
65
|
-
const client = mockClient({ appFieldOptions: async () => ({ fields: {} }) });
|
|
66
|
-
await expect(dispatchRpc(client, { app_id: "app_x", op: "field_options", payload: {} })).rejects.toThrow(/alias/);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
describe("dispatchRpc — comment ops", () => {
|
|
70
|
-
it("comments.list forwards to appGetRecordComments, wrapped as { comments }", async () => {
|
|
71
|
-
const comments = [{ id: "cmt_1", content: "hi" }];
|
|
72
|
-
const client = mockClient({ appGetRecordComments: async () => comments });
|
|
73
|
-
const result = await dispatchRpc(client, {
|
|
74
|
-
app_id: "app_x",
|
|
75
|
-
op: "comments.list",
|
|
76
|
-
payload: { record_id: "rec_1" },
|
|
77
|
-
});
|
|
78
|
-
expect(result).toEqual({ comments });
|
|
79
|
-
});
|
|
80
|
-
it("comments.counts forwards to appGetTableCommentCounts (counts, no content)", async () => {
|
|
81
|
-
const counts = { rec_1: 2, rec_2: 1 };
|
|
82
|
-
const client = mockClient({ appGetTableCommentCounts: async () => counts });
|
|
83
|
-
const result = await dispatchRpc(client, {
|
|
84
|
-
app_id: "app_x",
|
|
85
|
-
op: "comments.counts",
|
|
86
|
-
payload: { table_id: "tbl_1" },
|
|
87
|
-
});
|
|
88
|
-
expect(result).toEqual(counts);
|
|
89
|
-
});
|
|
90
|
-
it("requires the right payload per comment op", async () => {
|
|
91
|
-
const client = mockClient({
|
|
92
|
-
appGetRecordComments: async () => [],
|
|
93
|
-
appGetTableCommentCounts: async () => ({}),
|
|
94
|
-
});
|
|
95
|
-
await expect(dispatchRpc(client, { app_id: "app_x", op: "comments.list", payload: {} })).rejects.toThrow(/record_id/);
|
|
96
|
-
await expect(dispatchRpc(client, { app_id: "app_x", op: "comments.counts", payload: {} })).rejects.toThrow(/table_id/);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
describe("serializeRpcResult — every RPC response body is valid JSON", () => {
|
|
100
|
-
it("serializes a void result (comments.delete) as null, never an empty body", () => {
|
|
101
|
-
expect(serializeRpcResult(undefined)).toBe("null");
|
|
102
|
-
expect(JSON.parse(serializeRpcResult(undefined))).toBeNull();
|
|
103
|
-
});
|
|
104
|
-
it("passes object results through", () => {
|
|
105
|
-
expect(JSON.parse(serializeRpcResult({ comments: [] }))).toEqual({ comments: [] });
|
|
106
|
-
});
|
|
107
|
-
});
|
package/dist/dev/server.d.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `lotics app dev` orchestrator.
|
|
3
|
-
*
|
|
4
|
-
* Two child processes managed in one Node lifecycle:
|
|
5
|
-
* 1. Vite dev server (npx vite --port <vite-port>) — child_process.spawn,
|
|
6
|
-
* stdio inherited so Vite's own logging surfaces to the developer.
|
|
7
|
-
* 2. node:http server on <port> serving:
|
|
8
|
-
* GET / → wrapper HTML (cached: no)
|
|
9
|
-
* POST /_rpc → JSON in, dispatched via rpc_handler, JSON out
|
|
10
|
-
* POST /_agent_run → SSE, piped from the run
|
|
11
|
-
* PUT /_upload/:id → file bytes in (relayed to storage)
|
|
12
|
-
* GET /_file/:token → file bytes out (relayed from storage)
|
|
13
|
-
* * → 404
|
|
14
|
-
*
|
|
15
|
-
* SIGINT (Ctrl-C) → kill Vite child, close HTTP server, exit 0.
|
|
16
|
-
*
|
|
17
|
-
* Why the byte relays exist: dev runs against the PROD bucket (there is no dev
|
|
18
|
-
* bucket), whose CORS allowlist holds the real app origins (`https://*.lotics.app`),
|
|
19
|
-
* not `http://localhost:<port>`. So a browser transfer straight to/from the presigned
|
|
20
|
-
* URL is blocked before it leaves the page — no upload could complete, and no preview
|
|
21
|
-
* engine (PDF/Word/Excel all FETCH the bytes) could read a file. Relaying through THIS
|
|
22
|
-
* server fixes both: Node has no same-origin policy, and the one cross-origin hop that
|
|
23
|
-
* remains (the app iframe reading from us) is OUR response to allow.
|
|
24
|
-
*
|
|
25
|
-
* Neither relay ever takes a destination from the client: the page sends a `file_id`
|
|
26
|
-
* or an opaque token, and the server transfers only to/from a URL IT minted or observed
|
|
27
|
-
* for that id. No client-controlled target ⇒ no SSRF surface, nothing to allowlist.
|
|
28
|
-
*
|
|
29
|
-
* Production is untouched — it transfers direct-to-storage, keeping every byte off the
|
|
30
|
-
* API server.
|
|
31
|
-
*/
|
|
32
|
-
import { type ChildProcess } from "node:child_process";
|
|
33
|
-
import { LoticsClient } from "../client.js";
|
|
34
|
-
export interface DevServerArgs {
|
|
35
|
-
projectDir: string;
|
|
36
|
-
app_id: string;
|
|
37
|
-
app_name: string;
|
|
38
|
-
workspace_id: string;
|
|
39
|
-
/** API URL the CLI is configured against — shown in the header banner. */
|
|
40
|
-
api_url: string;
|
|
41
|
-
/** Preferred wrapper port. If taken, auto-picks a free one. */
|
|
42
|
-
port?: number;
|
|
43
|
-
/** Preferred Vite port. If taken, auto-picks a free one. */
|
|
44
|
-
vitePort?: number;
|
|
45
|
-
client: LoticsClient;
|
|
46
|
-
/** From `package.json#lotics.capabilities.comments` — fed to the `context` op
|
|
47
|
-
* so `useComments` reflects the manifest in the dev loop. */
|
|
48
|
-
commentsEnabled?: boolean;
|
|
49
|
-
}
|
|
50
|
-
export interface DevServerHandle {
|
|
51
|
-
port: number;
|
|
52
|
-
vitePort: number;
|
|
53
|
-
/** Resolves when both Vite + HTTP server are listening. */
|
|
54
|
-
ready: Promise<void>;
|
|
55
|
-
/** Stops Vite + HTTP server. Safe to call multiple times. */
|
|
56
|
-
stop: () => Promise<void>;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Serialize an RPC result for the HTTP response body. Ops with no return
|
|
60
|
-
* value (e.g. comments.delete) resolve `undefined` — in production they
|
|
61
|
-
* travel over postMessage where undefined structured-clones fine, but
|
|
62
|
-
* `JSON.stringify(undefined)` is not a string, so the body would be EMPTY
|
|
63
|
-
* and the wrapper's `JSON.parse` throws "Unexpected end of JSON input".
|
|
64
|
-
* Serialize those as `null`.
|
|
65
|
-
*/
|
|
66
|
-
export declare function serializeRpcResult(result: unknown): string;
|
|
67
|
-
export declare function startDevServer(args: DevServerArgs): Promise<DevServerHandle>;
|
|
68
|
-
/** Used by the CLI command for cross-platform `open <url>`. Best-effort —
|
|
69
|
-
* if the platform opener isn't installed (common in WSL, headless CI, some
|
|
70
|
-
* Linux minimal images), we log a note and keep the dev server running so
|
|
71
|
-
* the developer can copy/paste the URL manually. */
|
|
72
|
-
export declare function openBrowser(url: string): void;
|
|
73
|
-
declare global {
|
|
74
|
-
type _UnusedChildProcess = ChildProcess;
|
|
75
|
-
}
|