@silverbulletmd/silverbullet 2.8.1 → 2.10.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.
Files changed (65) hide show
  1. package/README.md +42 -25
  2. package/client/plugos/hooks/syscall.ts +24 -10
  3. package/client/plugos/syscalls/asset.ts +61 -23
  4. package/client/plugos/syscalls/clientStore.ts +32 -6
  5. package/client/plugos/syscalls/client_code_widget.ts +9 -4
  6. package/client/plugos/syscalls/code_widget.ts +26 -11
  7. package/client/plugos/syscalls/config.ts +146 -30
  8. package/client/plugos/syscalls/datastore.ts +146 -40
  9. package/client/plugos/syscalls/editor.ts +1432 -700
  10. package/client/plugos/syscalls/event.ts +46 -13
  11. package/client/plugos/syscalls/fetch.ts +123 -82
  12. package/client/plugos/syscalls/index.ts +243 -114
  13. package/client/plugos/syscalls/jsonschema.ts +107 -8
  14. package/client/plugos/syscalls/language.ts +38 -12
  15. package/client/plugos/syscalls/markdown.ts +166 -50
  16. package/client/plugos/syscalls/mq.ts +93 -20
  17. package/client/plugos/syscalls/schema_introspection.ts +33 -0
  18. package/client/plugos/syscalls/service_registry.ts +74 -20
  19. package/client/plugos/syscalls/shell.ts +51 -20
  20. package/client/plugos/syscalls/space.ts +169 -113
  21. package/client/plugos/syscalls/sync.ts +50 -19
  22. package/client/plugos/syscalls/system.ts +236 -139
  23. package/client/plugos/system.ts +21 -5
  24. package/client/space_lua/api_documentation.ts +107 -0
  25. package/client/space_lua/ast.ts +13 -0
  26. package/client/space_lua/eval.ts +12 -4
  27. package/client/space_lua/parse.ts +254 -119
  28. package/client/space_lua/pretty_print.ts +465 -0
  29. package/client/space_lua/query_collection.ts +3 -3
  30. package/client/space_lua/render_widget.ts +156 -0
  31. package/client/space_lua/runtime.ts +83 -22
  32. package/client/space_lua/stdlib/crypto.ts +8 -3
  33. package/client/space_lua/stdlib/encoding.ts +27 -9
  34. package/client/space_lua/stdlib/js.ts +137 -22
  35. package/client/space_lua/stdlib/load.ts +26 -17
  36. package/client/space_lua/stdlib/math.ts +323 -118
  37. package/client/space_lua/stdlib/net.ts +57 -9
  38. package/client/space_lua/stdlib/os.ts +111 -44
  39. package/client/space_lua/stdlib/space_lua.ts +397 -13
  40. package/client/space_lua/stdlib/string.ts +282 -104
  41. package/client/space_lua/stdlib/string_pack.ts +58 -19
  42. package/client/space_lua/stdlib/table.ts +195 -40
  43. package/client/space_lua/stdlib.ts +336 -94
  44. package/client/space_lua/syscalls.ts +270 -0
  45. package/dist/plug-compile.js +1 -1
  46. package/package.json +12 -4
  47. package/plug-api/lib/ref.ts +96 -0
  48. package/plug-api/lib/shortcut.ts +2 -1
  49. package/plug-api/syscalls/config.ts +5 -3
  50. package/plug-api/syscalls/editor.ts +23 -2
  51. package/plug-api/syscalls/index.ts +24 -1
  52. package/plug-api/syscalls/jsonschema.ts +9 -0
  53. package/plug-api/syscalls/lua.ts +28 -1
  54. package/plug-api/syscalls/shell.ts +0 -1
  55. package/plug-api/syscalls/system.ts +7 -0
  56. package/plug-api/system_mock.ts +2 -2
  57. package/plug-api/types/config.ts +1 -1
  58. package/plug-api/types/index.ts +52 -1
  59. package/plug-api/types/manifest.ts +22 -4
  60. package/plug-api/ui/cx.ts +4 -0
  61. package/plug-api/ui/index.ts +22 -0
  62. package/plug-api/ui/panel_styles.ts +33 -0
  63. package/plug-api/ui/slugify.ts +44 -0
  64. package/plugs/builtin_plugs.ts +1 -0
  65. package/client/plugos/syscalls/lua.ts +0 -58
@@ -10,6 +10,7 @@ import {
10
10
  type MarkdownExpandOptions,
11
11
  } from "../../markdown_renderer/inline.ts";
12
12
  import type { Client } from "../../client.ts";
13
+ import { bakeSectionsInText } from "../../baked_sections/bake.ts";
13
14
  import {
14
15
  type MarkdownRenderOptions,
15
16
  renderMarkdownToHtml,
@@ -22,62 +23,177 @@ import * as TagConstants from "../../../plugs/index/constants.ts";
22
23
 
23
24
  export function markdownSyscalls(client: Client): SysCallMapping {
24
25
  return {
25
- "markdown.parseMarkdown": (_ctx, text: string): ParseTree => {
26
- return parse(markdownLanguageWithUserExtensions(client), text);
26
+ "markdown.parseMarkdown": {
27
+ callback: (_ctx, text: string): ParseTree => {
28
+ return parse(markdownLanguageWithUserExtensions(client), text);
29
+ },
30
+ description: "Parses Markdown text into a syntax tree.",
31
+ parameters: [
32
+ { name: "text", type: "string", description: "Markdown source." },
33
+ ],
34
+ returns: [{ type: "table", description: "Parsed Markdown tree." }],
35
+ examples: [{ code: 'local tree = markdown.parseMarkdown("# Title")' }],
27
36
  },
28
- "markdown.renderParseTree": (_ctx, tree: ParseTree): string => {
29
- return renderToText(tree);
37
+ "markdown.renderParseTree": {
38
+ callback: (_ctx, tree: ParseTree): string => {
39
+ return renderToText(tree);
40
+ },
41
+ description: "Renders a Markdown syntax tree back to source text.",
42
+ parameters: [
43
+ { name: "tree", type: "table", description: "Markdown syntax tree." },
44
+ ],
45
+ returns: [{ type: "string", description: "Rendered Markdown." }],
46
+ examples: [
47
+ {
48
+ code: 'local tree = markdown.parseMarkdown("# Title")\nprint(markdown.renderParseTree(tree))',
49
+ },
50
+ ],
30
51
  },
31
- "markdown.expandMarkdown": async (
32
- _ctx,
33
- treeOrText: ParseTree | string,
34
- options?: MarkdownExpandOptions,
35
- ): Promise<ParseTree | string> => {
36
- const outputString = typeof treeOrText === "string";
37
- if (typeof treeOrText === "string") {
38
- treeOrText = parse(
39
- markdownLanguageWithUserExtensions(client),
52
+ "markdown.expandMarkdown": {
53
+ callback: async (
54
+ _ctx,
55
+ treeOrText: ParseTree | string,
56
+ options?: MarkdownExpandOptions,
57
+ ): Promise<ParseTree | string> => {
58
+ const outputString = typeof treeOrText === "string";
59
+ if (typeof treeOrText === "string") {
60
+ treeOrText = parse(
61
+ markdownLanguageWithUserExtensions(client),
62
+ treeOrText,
63
+ );
64
+ }
65
+ const result = await expandMarkdownWithClient(
66
+ client,
40
67
  treeOrText,
68
+ options,
41
69
  );
42
- }
43
- const result = await expandMarkdownWithClient(
44
- client,
45
- treeOrText,
46
- options,
47
- );
48
- if (outputString) {
49
- return renderToText(result);
50
- } else {
51
- return result;
52
- }
70
+ if (outputString) {
71
+ return renderToText(result);
72
+ } else {
73
+ return result;
74
+ }
75
+ },
76
+ description:
77
+ "Expands Markdown transclusions, Lua directives, and task references.",
78
+ signatures: [
79
+ "markdown.expandMarkdown(text, options?)",
80
+ "markdown.expandMarkdown(tree, options?)",
81
+ ],
82
+ parameters: [
83
+ { name: "textOrTree", description: "Markdown text or parsed tree." },
84
+ {
85
+ name: "options",
86
+ type: "table",
87
+ description:
88
+ "Expansion switches: expandTransclusions, expandLuaDirectives, and rewriteTasks; all default to true.",
89
+ optional: true,
90
+ },
91
+ ],
92
+ returns: [
93
+ { description: "Expanded text or tree, matching the input form." },
94
+ ],
95
+ examples: [
96
+ {
97
+ code: 'local expanded = markdown.expandMarkdown("This is some Lua: ${1 + 2}")',
98
+ },
99
+ ],
53
100
  },
54
- "markdown.markdownToHtml": async (
55
- _ctx,
56
- text: string,
57
- options: MarkdownRenderOptions = {},
58
- ) => {
59
- let mdTree = parse(markdownLanguageWithUserExtensions(client), text);
60
- if (options.expand) {
61
- mdTree = await expandMarkdownWithClient(client, mdTree);
62
- }
63
- if (!options.resolveTagHref) {
64
- options.resolveTagHref = (tagName: string) => {
65
- return client.config.get<string | null>(
66
- ["tags", tagName, "tagPage"],
67
- null,
68
- ) ?? TagConstants.tagPrefix + tagName;
69
- };
70
- }
71
- return renderMarkdownToHtml(mdTree, options);
101
+ "markdown.markdownToHtml": {
102
+ callback: async (
103
+ _ctx,
104
+ text: string,
105
+ options: MarkdownRenderOptions = {},
106
+ ) => {
107
+ let mdTree = parse(markdownLanguageWithUserExtensions(client), text);
108
+ if (options.expand) {
109
+ mdTree = await expandMarkdownWithClient(client, mdTree);
110
+ }
111
+ if (!options.resolveTagHref) {
112
+ options.resolveTagHref = (tagName: string) => {
113
+ return (
114
+ client.config.get<string | null>(
115
+ ["tags", tagName, "tagPage"],
116
+ null,
117
+ ) ?? TagConstants.tagPrefix + tagName
118
+ );
119
+ };
120
+ }
121
+ return renderMarkdownToHtml(mdTree, options);
122
+ },
123
+ description: "Renders Markdown text to HTML.",
124
+ parameters: [
125
+ { name: "text", type: "string", description: "Markdown source." },
126
+ {
127
+ name: "options",
128
+ type: "table",
129
+ description: "HTML rendering options.",
130
+ optional: true,
131
+ },
132
+ ],
133
+ returns: [{ type: "string", description: "Rendered HTML." }],
134
+ examples: [{ code: 'local html = markdown.markdownToHtml("# Title")' }],
72
135
  },
73
- "markdown.objectsToTable": (
74
- _ctx,
75
- data: any[],
76
- options: {
77
- renderCell?: (val: any, key: string) => Promise<any> | any;
78
- } = {},
79
- ) => {
80
- return jsonToMDTable(data, options.renderCell || refCellTransformer);
136
+ // Re-bake every baked section (`<!--#lua EXPR -->` … `<!--/lua-->`) in
137
+ // `text` and return the updated markdown. Pure transform (no editor) — the
138
+ // text-level counterpart of the "Baked Sections: Update" command. `pageName`
139
+ // sets the `currentPage` context for the evaluated expressions.
140
+ "markdown.bakeSections": {
141
+ callback: (_ctx, text: string, pageName?: string): Promise<string> => {
142
+ return bakeSectionsInText(client, text, pageName);
143
+ },
144
+ description:
145
+ "Re-evaluates all baked Lua sections in Markdown text; sections that error or only render as HTML are left unchanged.",
146
+ parameters: [
147
+ {
148
+ name: "text",
149
+ type: "string",
150
+ description: "Markdown containing baked sections.",
151
+ },
152
+ {
153
+ name: "pageName",
154
+ type: "string",
155
+ description: "Page used as currentPage during evaluation.",
156
+ optional: true,
157
+ },
158
+ ],
159
+ returns: [
160
+ {
161
+ type: "string",
162
+ description: "Markdown with updated baked section bodies.",
163
+ },
164
+ ],
165
+ examples: [
166
+ {
167
+ code: 'local text = "Total: <!--#lua 1 + 2 -->\\nold\\n<!--/lua-->"\nprint(markdown.bakeSections(text))',
168
+ },
169
+ ],
170
+ },
171
+ "markdown.objectsToTable": {
172
+ callback: (
173
+ _ctx,
174
+ data: any[],
175
+ options: {
176
+ renderCell?: (val: any, key: string) => Promise<any> | any;
177
+ } = {},
178
+ ) => {
179
+ return jsonToMDTable(data, options.renderCell || refCellTransformer);
180
+ },
181
+ description: "Formats a list of objects as a Markdown table.",
182
+ parameters: [
183
+ { name: "data", type: "table", description: "Rows to render." },
184
+ {
185
+ name: "options",
186
+ type: "table",
187
+ description: "Optional renderCell callback.",
188
+ optional: true,
189
+ },
190
+ ],
191
+ returns: [{ type: "string", description: "Markdown table." }],
192
+ examples: [
193
+ {
194
+ code: 'local tableText = markdown.objectsToTable({{name = "Pete", age = 20}})',
195
+ },
196
+ ],
81
197
  },
82
198
  };
83
199
  }
@@ -11,34 +11,107 @@ export function mqSyscalls(mq: DataStoreMQ): SysCallMapping {
11
11
  /**
12
12
  * Define a Lua event listener
13
13
  */
14
- "mq.subscribe": (_ctx, def: MQListenerSpec) => {
15
- def.autoAck = def.autoAck !== false;
16
- // console.log("Registering Lua event listener: ", def.name);
17
- client.config.insert(["mqSubscriptions", def.queue], def);
14
+ "mq.subscribe": {
15
+ callback: (_ctx, def: MQListenerSpec) => {
16
+ def.autoAck = def.autoAck !== false;
17
+ // console.log("Registering Lua event listener: ", def.name);
18
+ client.config.insert(["mqSubscriptions", def.queue], def);
19
+ },
20
+ description: "Subscribes a Space Lua callback to a message queue.",
21
+ parameters: [
22
+ {
23
+ name: "spec",
24
+ type: "table",
25
+ description:
26
+ "Subscription with queue, optional batchSize and pollInterval, autoAck (default true), and a run(messages) callback; each message has queue, id, and body fields.",
27
+ },
28
+ ],
29
+ examples: [
30
+ {
31
+ code: 'mq.subscribe { queue = "tasks", batchSize = 1, run = function(messages) print(messages[1].body) end }',
32
+ },
33
+ ],
18
34
  },
19
- "mq.send": (_ctx, queue: string, body: any) => {
20
- return mq.send(queue, body);
35
+ "mq.send": {
36
+ callback: (_ctx, queue: string, body: any) => {
37
+ return mq.send(queue, body);
38
+ },
39
+ description: "Sends a message to a queue.",
40
+ parameters: [
41
+ { name: "queue", type: "string", description: "Queue name." },
42
+ { name: "body", description: "Message body." },
43
+ ],
44
+ examples: [{ code: 'mq.send("tasks", "my task")' }],
21
45
  },
22
- "mq.batchSend": (_ctx, queue: string, bodies: any[]) => {
23
- return mq.batchSend(queue, bodies);
46
+ "mq.batchSend": {
47
+ callback: (_ctx, queue: string, bodies: any[]) => {
48
+ return mq.batchSend(queue, bodies);
49
+ },
50
+ description: "Sends multiple messages to a queue in one operation.",
51
+ parameters: [
52
+ { name: "queue", type: "string", description: "Queue name." },
53
+ { name: "bodies", type: "table", description: "Message bodies." },
54
+ ],
24
55
  },
25
- "mq.ack": (_ctx, queue: string, id: string) => {
26
- return mq.ack(queue, id);
56
+ "mq.ack": {
57
+ callback: (_ctx, queue: string, id: string) => {
58
+ return mq.ack(queue, id);
59
+ },
60
+ description: "Acknowledges one queue message as processed.",
61
+ parameters: [
62
+ { name: "queue", type: "string", description: "Queue name." },
63
+ { name: "id", type: "string", description: "Message ID." },
64
+ ],
27
65
  },
28
- "mq.batchAck": (_ctx, queue: string, ids: string[]) => {
29
- return mq.batchAck(queue, ids);
66
+ "mq.batchAck": {
67
+ callback: (_ctx, queue: string, ids: string[]) => {
68
+ return mq.batchAck(queue, ids);
69
+ },
70
+ description: "Acknowledges multiple queue messages as processed.",
71
+ parameters: [
72
+ { name: "queue", type: "string", description: "Queue name." },
73
+ { name: "ids", type: "table", description: "Message IDs." },
74
+ ],
30
75
  },
31
- "mq.flushQueue": (_ctx, queue: string) => {
32
- return mq.flushQueue(queue);
76
+ "mq.flushQueue": {
77
+ callback: (_ctx, queue: string) => {
78
+ return mq.flushQueue(queue);
79
+ },
80
+ description: "Removes all messages from a queue.",
81
+ parameters: [
82
+ { name: "queue", type: "string", description: "Queue name." },
83
+ ],
33
84
  },
34
- "mq.flushAllQueues": () => {
35
- return mq.flushAllQueues();
85
+ "mq.flushAllQueues": {
86
+ callback: () => {
87
+ return mq.flushAllQueues();
88
+ },
89
+ description: "Removes all messages from every queue.",
36
90
  },
37
- "mq.getQueueStats": (_ctx, queue: string) => {
38
- return mq.getQueueStats(queue);
91
+ "mq.getQueueStats": {
92
+ callback: (_ctx, queue: string) => {
93
+ return mq.getQueueStats(queue);
94
+ },
95
+ description:
96
+ "Gets queued, processing, and dead-letter counts for a queue.",
97
+ parameters: [
98
+ {
99
+ name: "queue",
100
+ type: "string",
101
+ description: "Queue name.",
102
+ optional: true,
103
+ },
104
+ ],
105
+ returns: [{ type: "table", description: "Queue statistics." }],
39
106
  },
40
- "mq.awaitEmptyQueue": (_ctx, queue: string) => {
41
- return mq.awaitEmptyQueue(queue);
107
+ "mq.awaitEmptyQueue": {
108
+ callback: (_ctx, queue: string) => {
109
+ return mq.awaitEmptyQueue(queue);
110
+ },
111
+ description: "Waits until a queue has no pending or processing messages.",
112
+ parameters: [
113
+ { name: "queue", type: "string", description: "Queue name." },
114
+ ],
42
115
  },
43
116
  };
44
117
  }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Pure schema-introspection helpers over the `["tags"]` config table.
3
+ *
4
+ * Returns raw JSON Schema objects; presentation-layer flattening (into typed
5
+ * property rows) happens in the `sb describe` CLI (describe.rs).
6
+ */
7
+
8
+ /**
9
+ * Returns a map of tag name → raw JSON Schema for every tag that defines a schema.
10
+ * Tags without a schema are omitted.
11
+ */
12
+ export function describeSchemas(
13
+ tags: Record<string, any>,
14
+ ): Record<string, unknown> {
15
+ const result: Record<string, unknown> = {};
16
+ for (const [name, def] of Object.entries(tags)) {
17
+ if (def?.schema != null) {
18
+ result[name] = def.schema;
19
+ }
20
+ }
21
+ return result;
22
+ }
23
+
24
+ /**
25
+ * Returns the raw JSON Schema for a single tag, or null if the tag is
26
+ * undefined or has no schema.
27
+ */
28
+ export function tagSchema(
29
+ tags: Record<string, any>,
30
+ tagName: string,
31
+ ): unknown | null {
32
+ return tags[tagName]?.schema ?? null;
33
+ }
@@ -12,29 +12,83 @@ export function serviceRegistrySyscalls(
12
12
  /**
13
13
  * Define a Lua event listener
14
14
  */
15
- "service.define": (_ctx, def: ServiceSpec) => {
16
- return serviceRegistry.define(def);
15
+ "service.define": {
16
+ callback: (_ctx, def: ServiceSpec) => {
17
+ return serviceRegistry.define(def);
18
+ },
19
+ description: "Defines a service that can be discovered by selector.",
20
+ parameters: [
21
+ {
22
+ name: "spec",
23
+ type: "table",
24
+ description: "Selector, match rule, and run callback.",
25
+ },
26
+ ],
27
+ examples: [
28
+ {
29
+ code: 'service.define { selector = "greeter", match = {}, run = function(name) return "Hello " .. name end }',
30
+ },
31
+ ],
17
32
  },
18
- "service.discover": (
19
- _ctx,
20
- selector: string,
21
- data: any,
22
- ): Promise<ServiceMatch[]> => {
23
- return serviceRegistry.discover(selector, data);
33
+ "service.discover": {
34
+ callback: (
35
+ _ctx,
36
+ selector: string,
37
+ data: any,
38
+ ): Promise<ServiceMatch[]> => {
39
+ return serviceRegistry.discover(selector, data);
40
+ },
41
+ description: "Discovers matching services sorted by descending priority.",
42
+ parameters: [
43
+ {
44
+ name: "selector",
45
+ type: "string",
46
+ description: "Service selector.",
47
+ },
48
+ { name: "data", description: "Value passed to match callbacks." },
49
+ ],
50
+ returns: [
51
+ { type: "table", description: "Matching service descriptors." },
52
+ ],
24
53
  },
25
- "service.invoke": (
26
- _ctx,
27
- service: ServiceMatch,
28
- data: any,
29
- ): Promise<any> => {
30
- return serviceRegistry.invoke(service, data);
54
+ "service.invoke": {
55
+ callback: (_ctx, service: ServiceMatch, data: any): Promise<any> => {
56
+ return serviceRegistry.invoke(service, data);
57
+ },
58
+ description: "Invokes a previously discovered service match.",
59
+ parameters: [
60
+ {
61
+ name: "match",
62
+ type: "table",
63
+ description: "Service match returned by service.discover.",
64
+ },
65
+ { name: "data", description: "Value passed to the service." },
66
+ ],
67
+ returns: [{ description: "Service result." }],
31
68
  },
32
- "service.invokeBestMatch": (
33
- _ctx,
34
- selector: string,
35
- data: any,
36
- ): Promise<any> => {
37
- return serviceRegistry.invokeBestMatch(selector, data);
69
+ "service.invokeBestMatch": {
70
+ callback: (_ctx, selector: string, data: any): Promise<any> => {
71
+ return serviceRegistry.invokeBestMatch(selector, data);
72
+ },
73
+ description:
74
+ "Discovers and invokes the highest-priority matching service.",
75
+ parameters: [
76
+ {
77
+ name: "selector",
78
+ type: "string",
79
+ description: "Service selector.",
80
+ },
81
+ {
82
+ name: "data",
83
+ description: "Value used for matching and invocation.",
84
+ },
85
+ ],
86
+ returns: [{ description: "Best matching service result." }],
87
+ examples: [
88
+ {
89
+ code: 'local greeting = service.invokeBestMatch("greeter", "Pete")',
90
+ },
91
+ ],
38
92
  },
39
93
  };
40
94
  }
@@ -4,28 +4,59 @@ import { fsEndpoint } from "../../spaces/constants.ts";
4
4
 
5
5
  export function shellSyscalls(client: Client): SysCallMapping {
6
6
  return {
7
- "shell.run": async (
8
- _ctx,
9
- cmd: string,
10
- args: string[],
11
- stdin?: string,
12
- ): Promise<{ stdout: string; stderr: string; code: number }> => {
13
- if (!client.httpSpacePrimitives) {
14
- throw new Error("Not supported in fully local mode");
15
- }
16
- const resp = client.httpSpacePrimitives.authenticatedFetch(
17
- buildShellUrl(client),
7
+ "shell.run": {
8
+ callback: async (
9
+ _ctx,
10
+ cmd: string,
11
+ args: string[],
12
+ stdin?: string,
13
+ ): Promise<{ stdout: string; stderr: string; code: number }> => {
14
+ if (!client.httpSpacePrimitives) {
15
+ throw new Error("Not supported in fully local mode");
16
+ }
17
+ const resp = client.httpSpacePrimitives.authenticatedFetch(
18
+ buildShellUrl(client),
19
+ {
20
+ method: "POST",
21
+ body: JSON.stringify({
22
+ cmd,
23
+ args,
24
+ stdin,
25
+ }),
26
+ },
27
+ );
28
+ const { code, stderr, stdout } = await (await resp).json();
29
+ return { code, stderr, stdout };
30
+ },
31
+ description: "Runs a shell command on the server and returns its output.",
32
+ parameters: [
33
+ { name: "command", type: "string", description: "Executable name." },
18
34
  {
19
- method: "POST",
20
- body: JSON.stringify({
21
- cmd,
22
- args,
23
- stdin,
24
- }),
35
+ name: "arguments",
36
+ type: "table",
37
+ description: "Command arguments.",
25
38
  },
26
- );
27
- const { code, stderr, stdout } = await (await resp).json();
28
- return { code, stderr, stdout };
39
+ {
40
+ name: "stdin",
41
+ type: "string",
42
+ description: "Text supplied on standard input.",
43
+ optional: true,
44
+ },
45
+ ],
46
+ returns: [
47
+ {
48
+ type: "table",
49
+ description: "stdout, stderr, and numeric exit code.",
50
+ },
51
+ ],
52
+ examples: [
53
+ {
54
+ code: 'local result = shell.run("ls", {"-l"})\nprint(result.stdout)',
55
+ },
56
+ {
57
+ code: 'local result = shell.run("cat", {}, "hello")\nprint(result.stdout)',
58
+ },
59
+ ],
29
60
  },
30
61
  };
31
62
  }