@mcpher/gas-fakes 1.2.10 → 1.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.RU.md +28 -0
- package/README.md +9 -2
- package/exgcp.sh +17 -1
- package/gas-fakes.js +47 -6
- package/main.js +6 -0
- package/package.json +5 -2
- package/src/index.js +2 -0
- package/src/services/lock/app.js +11 -0
- package/src/services/lock/fakelock.js +65 -0
- package/src/services/lock/fakelockservice.js +37 -0
package/README.RU.md
CHANGED
|
@@ -331,3 +331,31 @@ const getParentsIterator = ({
|
|
|
331
331
|
- [this file](README.md)
|
|
332
332
|
- [named colors](named-colors.md)
|
|
333
333
|
- [sandbox](sandbox.md)
|
|
334
|
+
|
|
335
|
+
## <img src="./logo.png" alt="gas-fakes logo" width="50" align="top"> Further Reading
|
|
336
|
+
|
|
337
|
+
- [getting started](GETTING_STARTED.md) - how to handle authentication for restricted scopes.
|
|
338
|
+
- [readme](README.md)
|
|
339
|
+
- [initial idea and thoughts](https://ramblings.mcpher.com/a-proof-of-concept-implementation-of-apps-script-environment-on-node/)
|
|
340
|
+
- [Inside the volatile world of a Google Document](https://ramblings.mcpher.com/inside-the-volatile-world-of-a-google-document/)
|
|
341
|
+
- [Apps Script Services on Node – using apps script libraries](https://ramblings.mcpher.com/apps-script-services-on-node-using-apps-script-libraries/)
|
|
342
|
+
- [Apps Script environment on Node – more services](https://ramblings.mcpher.com/apps-script-environment-on-node-more-services/)
|
|
343
|
+
- [Turning async into synch on Node using workers](https://ramblings.mcpher.com/turning-async-into-synch-on-node-using-workers/)
|
|
344
|
+
- [All about Apps Script Enums and how to fake them](https://ramblings.mcpher.com/all-about-apps-script-enums-and-how-to-fake-them/)
|
|
345
|
+
- [Russian version](README.RU.md) ([credit Alex Ivanov](https://github.com/oshliaer)) - needs updating
|
|
346
|
+
- [colaborators](collaborators.md) - additional information for collaborators
|
|
347
|
+
- [oddities](oddities.md) - a collection of oddities uncovered during this project
|
|
348
|
+
- [gemini](gemini-observations.md) - some reflections and experiences on using gemini to help code large projects
|
|
349
|
+
- [named colors](named-colors.md)
|
|
350
|
+
- [sandbox](sandbox.md)
|
|
351
|
+
- [named range identity](named-range-identity.md)
|
|
352
|
+
- [adc and restricted scopes](https://ramblings.mcpher.com/how-to-allow-access-to-sensitive-scopes-with-application-default-credentials/)
|
|
353
|
+
- [push test pull](pull-test-push.md)
|
|
354
|
+
- [gas fakes cli](gas-fakes-cli.md)
|
|
355
|
+
- [sharing cache and properties between gas-fakes and live apps script](https://ramblings.mcpher.com/sharing-cache-and-properties-between-gas-fakes-and-live-apps-script/)
|
|
356
|
+
- [gas-fakes-cli now has built in mcp server and gemini extension](https://ramblings.mcpher.com/gas-fakes-cli-now-has-built-in-mcp-server-and-gemini-extension/)
|
|
357
|
+
- [gas-fakes CLI: Run apps script code directly from your terminal](https://ramblings.mcpher.com/gas-fakes-cli-run-apps-script-code-directly-from-your-terminal/)
|
|
358
|
+
- [How to allow access to sensitive scopes with Application Default Credentials](https://ramblings.mcpher.com/how-to-allow-access-to-sensitive-scopes-with-application-default-credentials/)
|
|
359
|
+
- [Supercharge Your Google Apps Script Caching with GasFlexCache](https://ramblings.mcpher.com/supercharge-your-google-apps-script-caching-with-gasflexcache/)
|
|
360
|
+
- [Fake-Sandbox for Google Apps Script: Granular controls.](https://ramblings.mcpher.com/fake-sandbox-for-google-apps-script-granular-controls/)
|
|
361
|
+
- [A Fake-Sandbox for Google Apps Script: Securely Executing Code Generated by Gemini CLI](https://ramblings.mcpher.com/gas-fakes-sandbox/)
|
package/README.md
CHANGED
|
@@ -150,6 +150,7 @@ For inspiration on pushing modified files to the IDE, see the togas.sh bash scri
|
|
|
150
150
|
## Help
|
|
151
151
|
|
|
152
152
|
As I mentioned earlier, to take this further, I'm going to need a lot of help to extend the methods and services supported - so if you feel this would be useful to you, and would like to collaborate, please ping me on bruce@mcpher.com and we'll talk.
|
|
153
|
+
|
|
153
154
|
## <img src="./logo.png" alt="gas-fakes logo" width="50" align="top"> Further Reading
|
|
154
155
|
|
|
155
156
|
- [getting started](GETTING_STARTED.md) - how to handle authentication for restricted scopes.
|
|
@@ -163,11 +164,17 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
|
|
|
163
164
|
- [Russian version](README.RU.md) ([credit Alex Ivanov](https://github.com/oshliaer)) - needs updating
|
|
164
165
|
- [colaborators](collaborators.md) - additional information for collaborators
|
|
165
166
|
- [oddities](oddities.md) - a collection of oddities uncovered during this project
|
|
166
|
-
- [gemini](gemini.md) - some reflections and experiences on using gemini to help code large projects
|
|
167
|
+
- [gemini](gemini-observations.md) - some reflections and experiences on using gemini to help code large projects
|
|
167
168
|
- [named colors](named-colors.md)
|
|
168
169
|
- [sandbox](sandbox.md)
|
|
169
170
|
- [named range identity](named-range-identity.md)
|
|
170
171
|
- [adc and restricted scopes](https://ramblings.mcpher.com/how-to-allow-access-to-sensitive-scopes-with-application-default-credentials/)
|
|
171
172
|
- [push test pull](pull-test-push.md)
|
|
172
173
|
- [gas fakes cli](gas-fakes-cli.md)
|
|
173
|
-
- [sharing cache and properties between gas-fakes and live apps script](https://ramblings.mcpher.com/sharing-cache-and-properties-between-gas-fakes-and-live-apps-script/)
|
|
174
|
+
- [sharing cache and properties between gas-fakes and live apps script](https://ramblings.mcpher.com/sharing-cache-and-properties-between-gas-fakes-and-live-apps-script/)
|
|
175
|
+
- [gas-fakes-cli now has built in mcp server and gemini extension](https://ramblings.mcpher.com/gas-fakes-cli-now-has-built-in-mcp-server-and-gemini-extension/)
|
|
176
|
+
- [gas-fakes CLI: Run apps script code directly from your terminal](https://ramblings.mcpher.com/gas-fakes-cli-run-apps-script-code-directly-from-your-terminal/)
|
|
177
|
+
- [How to allow access to sensitive scopes with Application Default Credentials](https://ramblings.mcpher.com/how-to-allow-access-to-sensitive-scopes-with-application-default-credentials/)
|
|
178
|
+
- [Supercharge Your Google Apps Script Caching with GasFlexCache](https://ramblings.mcpher.com/supercharge-your-google-apps-script-caching-with-gasflexcache/)
|
|
179
|
+
- [Fake-Sandbox for Google Apps Script: Granular controls.](https://ramblings.mcpher.com/fake-sandbox-for-google-apps-script-granular-controls/)
|
|
180
|
+
- [A Fake-Sandbox for Google Apps Script: Securely Executing Code Generated by Gemini CLI](https://ramblings.mcpher.com/gas-fakes-sandbox/)
|
package/exgcp.sh
CHANGED
|
@@ -18,6 +18,8 @@ fi
|
|
|
18
18
|
|
|
19
19
|
# Read the GCP_PROJECT_ID, remove quotes, and handle potential carriage returns
|
|
20
20
|
GCP_PROJECT_ID_VALUE=$(grep -E '^GCP_PROJECT_ID=' "$ENV_FILE" | cut -d '=' -f2 | tr -d '"\r')
|
|
21
|
+
GEMINI_API_KEY_VALUE=$(grep -E '^GEMINI_API_KEY=' "$ENV_FILE" | cut -d '=' -f2 | tr -d '"\r')
|
|
22
|
+
GEMINI_MODEL_VALUE=$(grep -E '^GEMINI_MODEL=' "$ENV_FILE" | cut -d '=' -f2 | tr -d '"\r')
|
|
21
23
|
|
|
22
24
|
# Check if a value was extracted
|
|
23
25
|
if [ -z "$GCP_PROJECT_ID_VALUE" ]; then
|
|
@@ -25,7 +27,21 @@ if [ -z "$GCP_PROJECT_ID_VALUE" ]; then
|
|
|
25
27
|
return 1
|
|
26
28
|
fi
|
|
27
29
|
|
|
30
|
+
if [ -z "GEMINI_API_KEY_VALUE" ]; then
|
|
31
|
+
echo "GEMINI_API_KEY not found or is empty in $ENV_FILE."
|
|
32
|
+
else
|
|
33
|
+
echo "exported: GEMINI_API_KEY"
|
|
34
|
+
export GEMINI_API_KEY="$GEMINI_API_KEY_VALUE"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if [ -z "GEMINI_MODEL_VALUE" ]; then
|
|
38
|
+
echo "GEMINI_MODEL not found or is empty in $ENV_FILE."
|
|
39
|
+
else
|
|
40
|
+
echo "exported: GEMINI_MODEL=$GEMINI_MODEL_VALUE"
|
|
41
|
+
export GEMINI_MODEL="$GEMINI_MODEL_VALUE"
|
|
42
|
+
fi
|
|
43
|
+
|
|
28
44
|
# Export the variable for the current session
|
|
29
45
|
export GOOGLE_CLOUD_PROJECT="$GCP_PROJECT_ID_VALUE"
|
|
30
46
|
|
|
31
|
-
echo "
|
|
47
|
+
echo "exported: GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT"
|
package/gas-fakes.js
CHANGED
|
@@ -14,11 +14,17 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
14
14
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
|
|
17
|
+
// sync the version with gas fakes code since they share a package.json
|
|
18
|
+
import { createRequire } from 'node:module';
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
20
|
+
const pjson = require('./package.json');
|
|
21
|
+
const VERSION = pjson.version;
|
|
22
|
+
|
|
17
23
|
// -----------------------------------------------------------------------------
|
|
18
24
|
// CONSTANTS & UTILITIES
|
|
19
25
|
// -----------------------------------------------------------------------------
|
|
20
26
|
|
|
21
|
-
const
|
|
27
|
+
const CLI_VERSION = "0.0.7";
|
|
22
28
|
const MCP_VERSION = "0.0.3";
|
|
23
29
|
const execAsync = promisify(exec);
|
|
24
30
|
|
|
@@ -155,7 +161,7 @@ function generateExecutionScript({ scriptText, useSandbox, sandboxConfig }) {
|
|
|
155
161
|
' await import("./main.js"); // This will trigger the fxInit call',
|
|
156
162
|
gasScript,
|
|
157
163
|
"}",
|
|
158
|
-
"runGas();",
|
|
164
|
+
"return runGas();",
|
|
159
165
|
].join("\n");
|
|
160
166
|
|
|
161
167
|
return { mainScript, gasScript };
|
|
@@ -170,8 +176,15 @@ function generateExecutionScript({ scriptText, useSandbox, sandboxConfig }) {
|
|
|
170
176
|
* @param {object} options The processed CLI options.
|
|
171
177
|
*/
|
|
172
178
|
async function executeGasScript(options) {
|
|
173
|
-
const {
|
|
174
|
-
|
|
179
|
+
const {
|
|
180
|
+
filename,
|
|
181
|
+
script,
|
|
182
|
+
display,
|
|
183
|
+
gfSettings,
|
|
184
|
+
useSandbox,
|
|
185
|
+
sandboxConfig,
|
|
186
|
+
args,
|
|
187
|
+
} = options;
|
|
175
188
|
|
|
176
189
|
const scriptText = filename ? fs.readFileSync(filename, "utf8") : script;
|
|
177
190
|
|
|
@@ -194,8 +207,18 @@ async function executeGasScript(options) {
|
|
|
194
207
|
configurable: true,
|
|
195
208
|
});
|
|
196
209
|
|
|
197
|
-
|
|
198
|
-
|
|
210
|
+
let res;
|
|
211
|
+
if (args) {
|
|
212
|
+
const gasFunction = new Function("args", mainScript);
|
|
213
|
+
res = await gasFunction(args);
|
|
214
|
+
} else {
|
|
215
|
+
const gasFunction = new Function(mainScript);
|
|
216
|
+
res = await gasFunction();
|
|
217
|
+
}
|
|
218
|
+
if (res) {
|
|
219
|
+
const output = typeof res == "string" ? res : JSON.stringify(res);
|
|
220
|
+
console.log(output); // Returned value from Google Apps Script.
|
|
221
|
+
}
|
|
199
222
|
}
|
|
200
223
|
|
|
201
224
|
// -----------------------------------------------------------------------------
|
|
@@ -428,6 +451,11 @@ async function main() {
|
|
|
428
451
|
"Display the generated script before execution.",
|
|
429
452
|
false
|
|
430
453
|
)
|
|
454
|
+
.option(
|
|
455
|
+
"-a, --args <string>",
|
|
456
|
+
`Arguments for the function of Google Apps Script. Provide it as a JSON string. The name of the argument is "args" as a fixed name. For example, when the function of GAS is \`function sample(args) { script }\`, you can provide the arguments like \`-a '{"key": "value"}'\`.`,
|
|
457
|
+
null
|
|
458
|
+
)
|
|
431
459
|
.action(async (options) => {
|
|
432
460
|
if (Object.keys(options).length === 0) {
|
|
433
461
|
program.help();
|
|
@@ -455,6 +483,18 @@ async function main() {
|
|
|
455
483
|
const sandboxConfig = buildSandboxConfig(options);
|
|
456
484
|
const useSandbox = !!options.sandbox || !!sandboxConfig;
|
|
457
485
|
|
|
486
|
+
let args = null;
|
|
487
|
+
if (options.args) {
|
|
488
|
+
try {
|
|
489
|
+
args = JSON.parse(
|
|
490
|
+
options.args.replace(/\n/g, "\\n").replace(/\r/g, "\\r")
|
|
491
|
+
);
|
|
492
|
+
} catch (err) {
|
|
493
|
+
console.error("Error: Invalid JSON provided to --args option.");
|
|
494
|
+
process.exit(1);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
458
498
|
await executeGasScript({
|
|
459
499
|
filename,
|
|
460
500
|
script,
|
|
@@ -462,6 +502,7 @@ async function main() {
|
|
|
462
502
|
useSandbox,
|
|
463
503
|
sandboxConfig,
|
|
464
504
|
gfSettings: settingsPath,
|
|
505
|
+
args,
|
|
465
506
|
});
|
|
466
507
|
});
|
|
467
508
|
|
package/main.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
// testing locally
|
|
2
|
+
// sync the version with gas fakes code since they share a package.json
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
const pjson = require('./package.json');
|
|
6
|
+
const VERSION = pjson.version;
|
|
7
|
+
console.log (`...gas-fakes version ${VERSION}`)
|
|
2
8
|
import './src/index.js'
|
package/package.json
CHANGED
|
@@ -30,11 +30,14 @@
|
|
|
30
30
|
},
|
|
31
31
|
"type": "module",
|
|
32
32
|
"scripts": {
|
|
33
|
-
"pub": "npm publish --access public"
|
|
33
|
+
"pub": "npm publish --access public",
|
|
34
|
+
"includes": "node ./doccreation/include-docs.js",
|
|
35
|
+
"progress": "cd ./doccreation && bash pipeline.sh",
|
|
36
|
+
"docs": "npm run includes && npm run progress"
|
|
34
37
|
},
|
|
35
38
|
"name": "@mcpher/gas-fakes",
|
|
36
39
|
"author": "bruce mcpherson",
|
|
37
|
-
"version": "1.2.
|
|
40
|
+
"version": "1.2.12",
|
|
38
41
|
"license": "MIT",
|
|
39
42
|
"main": "main.js",
|
|
40
43
|
"description": "A proof of concept implementation of Apps Script Environment on Node",
|
package/src/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
import './services/scriptapp/app.js'
|
|
2
3
|
|
|
3
4
|
import './services/driveapp/app.js'
|
|
@@ -22,5 +23,6 @@ import './services/advforms/app.js'
|
|
|
22
23
|
import './services/formapp/app.js'
|
|
23
24
|
import './services/slidesapp/app.js'
|
|
24
25
|
import './services/mimetype/app.js'
|
|
26
|
+
import './services/lock/app.js'
|
|
25
27
|
// should be last
|
|
26
28
|
import './services/stores/app.js'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* the idea here is to create an empty global entry for the singleton
|
|
5
|
+
* but only load it when it is actually used.
|
|
6
|
+
*/
|
|
7
|
+
import { newFakeLockService as maker } from './fakelockservice.js';
|
|
8
|
+
import { lazyLoaderApp } from '../common/lazyloader.js'
|
|
9
|
+
|
|
10
|
+
let _app = null;
|
|
11
|
+
_app = lazyLoaderApp(_app, 'LockService', maker)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* the lock service us meaningless here as we are running on node single threaded
|
|
6
|
+
* in apps script this would lock shared code that was being shared by multiple scripts
|
|
7
|
+
* so this is all provided for compatibility only
|
|
8
|
+
*/
|
|
9
|
+
class FakeLock {
|
|
10
|
+
constructor(domain) {
|
|
11
|
+
this.__fakeObjectType = 'Lock';
|
|
12
|
+
this.__domain = domain
|
|
13
|
+
this.__locked = false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns true if the lock was acquired.
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
hasLock() {
|
|
21
|
+
return this.__locked;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Releases the lock.
|
|
26
|
+
*/
|
|
27
|
+
releaseLock() {
|
|
28
|
+
this.__locked = false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Attempts to acquire the lock.
|
|
33
|
+
* @param {number} timeoutInMillis
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
tryLock(timeoutInMillis) {
|
|
37
|
+
if (this.hasLock()) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
// In a single-threaded fake, we can't wait. We fail only if timeout is negative.
|
|
41
|
+
if (timeoutInMillis < 0) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
this.__locked = true;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Attempts to acquire the lock, throwing an exception on timeout.
|
|
50
|
+
* @param {number} timeoutInMillis
|
|
51
|
+
*/
|
|
52
|
+
waitLock(timeoutInMillis) {
|
|
53
|
+
if (this.hasLock()) {
|
|
54
|
+
return; // Already acquired, no need to wait
|
|
55
|
+
}
|
|
56
|
+
// In a single-threaded fake, we can't wait. We fail only if timeout is negative.
|
|
57
|
+
if (timeoutInMillis < 0) {
|
|
58
|
+
throw new Error(`Lock timeout: another process was holding the lock for too long.`);
|
|
59
|
+
}
|
|
60
|
+
this.__locked = true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
export const newFakeLock = (...args) => Proxies.guard(new FakeLock(...args));
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeLock } from './fakelock.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* domains of lock supported
|
|
6
|
+
* @enum {string}
|
|
7
|
+
*/
|
|
8
|
+
const LockDomain = Object.freeze({
|
|
9
|
+
SCRIPT: 'SCRIPT',
|
|
10
|
+
USER: 'USER',
|
|
11
|
+
DOCUMENT: 'DOCUMENT'
|
|
12
|
+
})
|
|
13
|
+
/**
|
|
14
|
+
* the lock service us meaningless here as we are running on node single threaded
|
|
15
|
+
* in apps script this would lock shared code that was being shared by multiple scripts
|
|
16
|
+
* so this is all provided for compatibility only
|
|
17
|
+
*/
|
|
18
|
+
class FakeLockService {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.__fakeObjectType = 'LockService';
|
|
21
|
+
}
|
|
22
|
+
getDocumentLock () {
|
|
23
|
+
// Per documentation, this should return null if not in the context of a document.
|
|
24
|
+
if (ScriptApp.__documentId) {
|
|
25
|
+
return newFakeLock(LockDomain.DOCUMENT);
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
getUserLock () {
|
|
30
|
+
return newFakeLock(LockDomain.USER)
|
|
31
|
+
}
|
|
32
|
+
getScriptLock () {
|
|
33
|
+
return newFakeLock(LockDomain.SCRIPT)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const newFakeLockService = (...args) => Proxies.guard(new FakeLockService(...args));
|