runline 0.12.0 → 0.14.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.
@@ -1,4 +1,6 @@
1
+ import * as t from "typebox";
1
2
  import { googleAccessToken } from "../../_shared/googleAuth.js";
3
+ import { Id, NonEmptyString, PositiveInteger, stringEnum, } from "../../_shared/googleSchemas.js";
2
4
  const SCRIPT_API = "https://script.googleapis.com/v1";
3
5
  const DRIVE_API = "https://www.googleapis.com/drive/v3";
4
6
  const SCOPES = [
@@ -7,6 +9,30 @@ const SCOPES = [
7
9
  "https://www.googleapis.com/auth/script.processes",
8
10
  "https://www.googleapis.com/auth/drive.metadata.readonly",
9
11
  ];
12
+ const scriptFileTypeSchema = stringEnum(["SERVER_JS", "HTML", "JSON"]);
13
+ const jsonValueSchema = t.Unsafe({
14
+ $defs: {
15
+ value: {
16
+ anyOf: [
17
+ { type: "null" },
18
+ { type: "string" },
19
+ { type: "number" },
20
+ { type: "boolean" },
21
+ { type: "array", items: { $ref: "#/$defs/value" } },
22
+ {
23
+ type: "object",
24
+ additionalProperties: { $ref: "#/$defs/value" },
25
+ },
26
+ ],
27
+ },
28
+ },
29
+ $ref: "#/$defs/value",
30
+ });
31
+ const scriptFileSchema = t.Object({
32
+ name: NonEmptyString,
33
+ type: scriptFileTypeSchema,
34
+ source: t.String(),
35
+ }, { additionalProperties: false });
10
36
  function accessToken(ctx) {
11
37
  return googleAccessToken(ctx, "googleAppsScript", SCOPES);
12
38
  }
@@ -48,10 +74,14 @@ export default function googleAppsScript(rl) {
48
74
  rl.registerAction("script.list", {
49
75
  access: "read",
50
76
  description: "List Apps Script projects in Drive (standalone scripts; bound scripts live inside their container and don't appear here).",
51
- inputSchema: {
52
- query: { type: "string", required: false, description: "Case-insensitive name substring filter." },
53
- pageSize: { type: "number", required: false, description: "Max results (default 50)." },
54
- },
77
+ inputSchema: t.Object({
78
+ query: t.Optional(t.String({ description: "Case-insensitive name substring filter." })),
79
+ pageSize: t.Optional(t.Integer({
80
+ minimum: 1,
81
+ maximum: 1000,
82
+ description: "Max results (default 50).",
83
+ })),
84
+ }, { additionalProperties: false }),
55
85
  async execute(input, ctx) {
56
86
  const params = new URLSearchParams({
57
87
  q: "mimeType='application/vnd.google-apps.script' and trashed=false",
@@ -71,7 +101,7 @@ export default function googleAppsScript(rl) {
71
101
  rl.registerAction("project.getContent", {
72
102
  access: "read",
73
103
  description: "Get all files of an Apps Script project (name, type, source).",
74
- inputSchema: { scriptId: { type: "string", required: true } },
104
+ inputSchema: t.Object({ scriptId: Id }, { additionalProperties: false }),
75
105
  async execute(input, ctx) {
76
106
  const res = await call(ctx, "GET", `${SCRIPT_API}/projects/${input.scriptId}/content`);
77
107
  return { scriptId: input.scriptId, files: res.files ?? [] };
@@ -80,10 +110,13 @@ export default function googleAppsScript(rl) {
80
110
  rl.registerAction("project.readFile", {
81
111
  access: "read",
82
112
  description: "Read one file's source from a project.",
83
- inputSchema: {
84
- scriptId: { type: "string", required: true },
85
- name: { type: "string", required: true, description: "File name without extension (e.g. 'Code', 'appsscript')." },
86
- },
113
+ inputSchema: t.Object({
114
+ scriptId: Id,
115
+ name: t.String({
116
+ minLength: 1,
117
+ description: "File name without extension (e.g. 'Code', 'appsscript').",
118
+ }),
119
+ }, { additionalProperties: false }),
87
120
  async execute(input, ctx) {
88
121
  const res = await call(ctx, "GET", `${SCRIPT_API}/projects/${input.scriptId}/content`);
89
122
  const file = (res.files ?? []).find((f) => f.name === input.name);
@@ -95,12 +128,12 @@ export default function googleAppsScript(rl) {
95
128
  rl.registerAction("file.edit", {
96
129
  access: "write",
97
130
  description: "Replace (or add) a single file's source, leaving other files untouched. Read-modify-write — the safe way to change code.",
98
- inputSchema: {
99
- scriptId: { type: "string", required: true },
100
- name: { type: "string", required: true },
101
- source: { type: "string", required: true },
102
- type: { type: "string", required: false, description: "SERVER_JS (default), HTML, or JSON (for appsscript)." },
103
- },
131
+ inputSchema: t.Object({
132
+ scriptId: Id,
133
+ name: NonEmptyString,
134
+ source: t.String(),
135
+ type: t.Optional(stringEnum(["SERVER_JS", "HTML", "JSON"])),
136
+ }, { additionalProperties: false }),
104
137
  async execute(input, ctx) {
105
138
  const cur = await call(ctx, "GET", `${SCRIPT_API}/projects/${input.scriptId}/content`);
106
139
  const files = cur.files ?? [];
@@ -118,10 +151,17 @@ export default function googleAppsScript(rl) {
118
151
  rl.registerAction("project.updateContent", {
119
152
  access: "write",
120
153
  description: "Replace the entire project file set. files = [{name, type, source}], must include the appsscript JSON manifest. Prefer file.edit for single changes.",
121
- inputSchema: {
122
- scriptId: { type: "string", required: true },
123
- files: { type: "array", required: true, description: "[{name, type: SERVER_JS|HTML|JSON, source}]" },
124
- },
154
+ inputSchema: t.Object({
155
+ scriptId: Id,
156
+ files: t.Array(scriptFileSchema, {
157
+ minItems: 1,
158
+ contains: t.Object({
159
+ name: t.Literal("appsscript"),
160
+ type: t.Literal("JSON"),
161
+ }),
162
+ description: "Complete project files, including the appsscript JSON manifest",
163
+ }),
164
+ }, { additionalProperties: false }),
125
165
  async execute(input, ctx) {
126
166
  const res = await call(ctx, "PUT", `${SCRIPT_API}/projects/${input.scriptId}/content`, { files: input.files });
127
167
  return { scriptId: input.scriptId, fileCount: (res.files ?? []).length };
@@ -130,10 +170,13 @@ export default function googleAppsScript(rl) {
130
170
  rl.registerAction("project.create", {
131
171
  access: "write",
132
172
  description: "Create a new Apps Script project. Pass parentId (a Drive file id, e.g. a Sheet) to bind it to that container.",
133
- inputSchema: {
134
- title: { type: "string", required: true },
135
- parentId: { type: "string", required: false, description: "Container Drive file id for a bound script." },
136
- },
173
+ inputSchema: t.Object({
174
+ title: NonEmptyString,
175
+ parentId: t.Optional(t.String({
176
+ minLength: 1,
177
+ description: "Container Drive file id for a bound script.",
178
+ })),
179
+ }, { additionalProperties: false }),
137
180
  async execute(input, ctx) {
138
181
  const payload = { title: input.title };
139
182
  if (input.parentId)
@@ -145,10 +188,10 @@ export default function googleAppsScript(rl) {
145
188
  rl.registerAction("version.create", {
146
189
  access: "write",
147
190
  description: "Create an immutable version of the project (needed before deploying).",
148
- inputSchema: {
149
- scriptId: { type: "string", required: true },
150
- description: { type: "string", required: false },
151
- },
191
+ inputSchema: t.Object({
192
+ scriptId: Id,
193
+ description: t.Optional(t.String()),
194
+ }, { additionalProperties: false }),
152
195
  async execute(input, ctx) {
153
196
  const res = await call(ctx, "POST", `${SCRIPT_API}/projects/${input.scriptId}/versions`, { description: input.description || "" });
154
197
  return { scriptId: input.scriptId, versionNumber: res.versionNumber, description: res.description };
@@ -157,12 +200,12 @@ export default function googleAppsScript(rl) {
157
200
  rl.registerAction("deployment.create", {
158
201
  access: "write",
159
202
  description: "Deploy a version. For function.run, deploy with an API-executable manifest (executionApi access).",
160
- inputSchema: {
161
- scriptId: { type: "string", required: true },
162
- versionNumber: { type: "number", required: true },
163
- description: { type: "string", required: false },
164
- manifestFileName: { type: "string", required: false, description: "Defaults to 'appsscript'." },
165
- },
203
+ inputSchema: t.Object({
204
+ scriptId: Id,
205
+ versionNumber: PositiveInteger,
206
+ description: t.Optional(t.String()),
207
+ manifestFileName: t.Optional(t.String({ minLength: 1, description: "Defaults to 'appsscript'." })),
208
+ }, { additionalProperties: false }),
166
209
  async execute(input, ctx) {
167
210
  const res = await call(ctx, "POST", `${SCRIPT_API}/projects/${input.scriptId}/deployments`, {
168
211
  versionNumber: input.versionNumber,
@@ -175,12 +218,14 @@ export default function googleAppsScript(rl) {
175
218
  rl.registerAction("function.run", {
176
219
  access: "write",
177
220
  description: "Run a function via scripts.run. Requires the project linked to a standard GCP project, the Apps Script API enabled, and an API-executable deployment (or devMode for the owner).",
178
- inputSchema: {
179
- scriptId: { type: "string", required: true },
180
- functionName: { type: "string", required: true },
181
- parameters: { type: "array", required: false, description: "Positional args for the function." },
182
- devMode: { type: "boolean", required: false, description: "Run latest saved code (owner only). Default true." },
183
- },
221
+ inputSchema: t.Object({
222
+ scriptId: Id,
223
+ functionName: NonEmptyString,
224
+ parameters: t.Optional(t.Array(jsonValueSchema)),
225
+ devMode: t.Optional(t.Boolean({
226
+ description: "Run latest saved code (owner only). Default true.",
227
+ })),
228
+ }, { additionalProperties: false }),
184
229
  async execute(input, ctx) {
185
230
  const res = await call(ctx, "POST", `${SCRIPT_API}/scripts/${input.scriptId}:run`, {
186
231
  function: input.functionName,
@@ -197,10 +242,10 @@ export default function googleAppsScript(rl) {
197
242
  rl.registerAction("process.list", {
198
243
  access: "read",
199
244
  description: "Recent executions for a project (status, function, times) — a log view.",
200
- inputSchema: {
201
- scriptId: { type: "string", required: true },
202
- pageSize: { type: "number", required: false, description: "Default 20." },
203
- },
245
+ inputSchema: t.Object({
246
+ scriptId: Id,
247
+ pageSize: t.Optional(t.Integer({ minimum: 1, maximum: 50, description: "Default 20." })),
248
+ }, { additionalProperties: false }),
204
249
  async execute(input, ctx) {
205
250
  const params = new URLSearchParams({
206
251
  "userProcessFilter.scriptId": input.scriptId,