@mcpher/gas-fakes 2.2.5 → 2.2.7
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.md +1 -1
- package/package.json +8 -4
- package/src/cli/setup.js +14 -8
- package/src/services/spreadsheetapp/fakecontainerinfo.js +58 -0
- package/src/services/spreadsheetapp/fakeembeddedchart.js +9 -1
- package/src/support/msgraph/msauth.js +64 -10
- package/exgcp.sh +0 -54
- package/introvideo.png +0 -0
- package/logo.png +0 -0
- package/plain-logo.png +0 -0
- package/testlib.sh +0 -2
package/README.md
CHANGED
|
@@ -196,7 +196,7 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
|
|
|
196
196
|
- [how libhandler works](libhandler.md)
|
|
197
197
|
- [article:using apps script libraries with gas-fakes](https://ramblings.mcpher.com/how-to-use-apps-script-libraries-directly-from-node/)
|
|
198
198
|
- [named range identity](named-range-identity.md)
|
|
199
|
-
- [
|
|
199
|
+
- [sensitive scopes with local authentication](senstive_scopes.md)
|
|
200
200
|
- [push test pull](pull-test-push.md)
|
|
201
201
|
- [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/)
|
|
202
202
|
- [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/)
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"node": ">=20.11.0"
|
|
4
4
|
},
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@azure/identity": "^4.13.
|
|
6
|
+
"@azure/identity": "^4.13.1",
|
|
7
7
|
"@mcpher/fake-gasenum": "^1.0.6",
|
|
8
8
|
"@mcpher/gas-flex-cache": "^1.1.5",
|
|
9
9
|
"@microsoft/microsoft-graph-client": "^3.0.7",
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
"archiver": "^7.0.1",
|
|
14
14
|
"commander": "^14.0.3",
|
|
15
15
|
"dotenv": "^17.3.1",
|
|
16
|
-
"fast-xml-parser": "^5.
|
|
16
|
+
"fast-xml-parser": "^5.5.8",
|
|
17
17
|
"get-stream": "^9.0.1",
|
|
18
|
+
"google-auth-library": "^10.6.2",
|
|
18
19
|
"googleapis": "^171.4.0",
|
|
19
20
|
"got": "^14.6.6",
|
|
20
21
|
"into-stream": "^9.1.0",
|
|
@@ -26,6 +27,9 @@
|
|
|
26
27
|
"unzipper": "^0.12.3",
|
|
27
28
|
"zod": "^4.3.6"
|
|
28
29
|
},
|
|
30
|
+
"overrides": {
|
|
31
|
+
"glob": "^13.0.6"
|
|
32
|
+
},
|
|
29
33
|
"type": "module",
|
|
30
34
|
"scripts": {
|
|
31
35
|
"pub": "npm publish --access public",
|
|
@@ -35,7 +39,7 @@
|
|
|
35
39
|
},
|
|
36
40
|
"name": "@mcpher/gas-fakes",
|
|
37
41
|
"author": "bruce mcpherson",
|
|
38
|
-
"version": "2.2.
|
|
42
|
+
"version": "2.2.7",
|
|
39
43
|
"license": "MIT",
|
|
40
44
|
"main": "main.js",
|
|
41
45
|
"description": "An implementation of the Google Workspace Apps Script runtime: Run native App Script Code on Node and Cloud Run",
|
|
@@ -44,4 +48,4 @@
|
|
|
44
48
|
"bin": {
|
|
45
49
|
"gas-fakes": "gas-fakes.js"
|
|
46
50
|
}
|
|
47
|
-
}
|
|
51
|
+
}
|
package/src/cli/setup.js
CHANGED
|
@@ -283,6 +283,7 @@ export async function initializeConfiguration(options = {}) {
|
|
|
283
283
|
"https://www.googleapis.com/auth/userinfo.email",
|
|
284
284
|
"openid",
|
|
285
285
|
"https://www.googleapis.com/auth/cloud-platform",
|
|
286
|
+
"https://www.googleapis.com/auth/drive.readonly",
|
|
286
287
|
];
|
|
287
288
|
responses.DEFAULT_SCOPES = DEFAULT_SCOPES_VALUES.join(",");
|
|
288
289
|
responses.EXTRA_SCOPES = manifestScopes
|
|
@@ -303,12 +304,12 @@ export async function initializeConfiguration(options = {}) {
|
|
|
303
304
|
initial: existingConfig.GOOGLE_SERVICE_ACCOUNT_NAME || "gas-fakes-sa",
|
|
304
305
|
},
|
|
305
306
|
{
|
|
306
|
-
type: "text",
|
|
307
|
+
type: responses.AUTH_TYPE === "adc" ? "text" : null,
|
|
307
308
|
name: "CLIENT_CREDENTIAL_FILE",
|
|
308
309
|
message: "Enter path to OAuth client credentials JSON (optional, required for restricted scopes with ADC)",
|
|
309
310
|
initial: existingConfig.CLIENT_CREDENTIAL_FILE || "",
|
|
310
311
|
}
|
|
311
|
-
|
|
312
|
+
];
|
|
312
313
|
|
|
313
314
|
const googleResponses = await prompts(googleQuestions);
|
|
314
315
|
if (typeof googleResponses.GOOGLE_CLOUD_PROJECT === "undefined") {
|
|
@@ -342,10 +343,10 @@ export async function initializeConfiguration(options = {}) {
|
|
|
342
343
|
message: "What type of Microsoft account are you using?",
|
|
343
344
|
choices: [
|
|
344
345
|
{ title: "Consumer (Personal, Outlook.com, Hotmail, etc.)", value: "consumers" },
|
|
345
|
-
{ title: "Business/Education (Work or School)", value: "organizations" },
|
|
346
|
+
{ title: "Business/Education (Work or School) ", value: "organizations" },
|
|
346
347
|
{ title: "Standard Multi-tenant", value: "common" }
|
|
347
348
|
],
|
|
348
|
-
initial: existingConfig.MS_GRAPH_TENANT_ID === "
|
|
349
|
+
initial: existingConfig.MS_GRAPH_TENANT_ID === "organizations" ? 1 : (existingConfig.MS_GRAPH_TENANT_ID === "common" ? 2 : 0)
|
|
349
350
|
});
|
|
350
351
|
|
|
351
352
|
if (typeof msAccountType.type === "undefined") {
|
|
@@ -604,13 +605,13 @@ export async function authenticateUser(options = {}) {
|
|
|
604
605
|
const msScopes = mapGasScopesToMsGraph(gasScopes);
|
|
605
606
|
|
|
606
607
|
try {
|
|
608
|
+
const tenantId = process.env.MS_GRAPH_TENANT_ID || 'consumers';
|
|
607
609
|
const azCmd = `az config set core.login_experience_v2=off && az login --allow-no-subscriptions --output none`;
|
|
608
610
|
|
|
609
611
|
console.log(`Executing: ${azCmd}`);
|
|
610
612
|
try {
|
|
611
613
|
runCommandSync(azCmd);
|
|
612
614
|
console.log(`\n\x1b[1;32mSuccess!\x1b[0m Azure CLI session discovered.`);
|
|
613
|
-
const tenantId = process.env.MS_GRAPH_TENANT_ID || 'consumers';
|
|
614
615
|
console.log(`Silent fallback is now enabled for: \x1b[1;36m${tenantId}\x1b[0m`);
|
|
615
616
|
} catch (e) {
|
|
616
617
|
console.error(`\x1b[1;31mAzure CLI Login failed.\x1b[0m`);
|
|
@@ -662,7 +663,11 @@ export async function authenticateUser(options = {}) {
|
|
|
662
663
|
...(EXTRA_SCOPES || "").split(","),
|
|
663
664
|
])).filter(s => s).join(",");
|
|
664
665
|
|
|
665
|
-
|
|
666
|
+
const adcScopes = AUTH_TYPE === "dwd"
|
|
667
|
+
? Array.from(new Set((DEFAULT_SCOPES || "").split(","))).filter(s => s).join(",")
|
|
668
|
+
: scopes;
|
|
669
|
+
|
|
670
|
+
console.log(`...requesting scopes: ${adcScopes}`);
|
|
666
671
|
|
|
667
672
|
const driveAccessFlag = "--enable-gdrive-access";
|
|
668
673
|
const activeConfig = AC || "default";
|
|
@@ -684,7 +689,7 @@ export async function authenticateUser(options = {}) {
|
|
|
684
689
|
runCommandSync(`gcloud auth login ${driveAccessFlag}`);
|
|
685
690
|
|
|
686
691
|
let clientFlag = "";
|
|
687
|
-
if (CLIENT_CREDENTIAL_FILE) {
|
|
692
|
+
if (AUTH_TYPE !== "dwd" && CLIENT_CREDENTIAL_FILE) {
|
|
688
693
|
const clientPath = path.resolve(process.cwd(), CLIENT_CREDENTIAL_FILE);
|
|
689
694
|
if (fs.existsSync(clientPath)) {
|
|
690
695
|
console.log(`...using client credentials from ${clientPath}`);
|
|
@@ -693,7 +698,8 @@ export async function authenticateUser(options = {}) {
|
|
|
693
698
|
}
|
|
694
699
|
|
|
695
700
|
console.log("Setting up Application Default Credentials (ADC)...");
|
|
696
|
-
|
|
701
|
+
const adcScopeFlag = `--scopes="${adcScopes}"`;
|
|
702
|
+
runCommandSync(`gcloud auth application-default login ${adcScopeFlag} ${clientFlag}`.trim());
|
|
697
703
|
runCommandSync(`gcloud auth application-default set-quota-project ${projectId}`);
|
|
698
704
|
|
|
699
705
|
// --- DWD Specific Setup (if configured) ---
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Proxies } from "../../support/proxies.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* create a new FakeContainerInfo instance
|
|
5
|
+
* @param {...any} args
|
|
6
|
+
* @returns {FakeContainerInfo}
|
|
7
|
+
*/
|
|
8
|
+
export const newFakeContainerInfo = (...args) => {
|
|
9
|
+
return Proxies.guard(new FakeContainerInfo(...args));
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Access to the chart's container position.
|
|
14
|
+
*/
|
|
15
|
+
export class FakeContainerInfo {
|
|
16
|
+
/**
|
|
17
|
+
* @param {object} overlayPosition The overlayPosition object from Sheets API
|
|
18
|
+
*/
|
|
19
|
+
constructor(overlayPosition) {
|
|
20
|
+
this.__overlayPosition = overlayPosition || {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns the column index where the drawing is anchored.
|
|
25
|
+
* @returns {number}
|
|
26
|
+
*/
|
|
27
|
+
getAnchorColumn() {
|
|
28
|
+
return (this.__overlayPosition.anchorCell?.columnIndex || 0) + 1;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Returns the row index where the drawing is anchored.
|
|
33
|
+
* @returns {number}
|
|
34
|
+
*/
|
|
35
|
+
getAnchorRow() {
|
|
36
|
+
return (this.__overlayPosition.anchorCell?.rowIndex || 0) + 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns the horizontal offset in pixels from the anchor column.
|
|
41
|
+
* @returns {number}
|
|
42
|
+
*/
|
|
43
|
+
getOffsetX() {
|
|
44
|
+
return this.__overlayPosition.offsetXPixels || 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Returns the vertical offset in pixels from the anchor row.
|
|
49
|
+
* @returns {number}
|
|
50
|
+
*/
|
|
51
|
+
getOffsetY() {
|
|
52
|
+
return this.__overlayPosition.offsetYPixels || 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
toString() {
|
|
56
|
+
return "ContainerInfo";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -2,6 +2,7 @@ import { Proxies } from "../../support/proxies.js";
|
|
|
2
2
|
import { notYetImplemented, signatureArgs } from "../../support/helpers.js";
|
|
3
3
|
import { batchUpdate } from "./sheetrangehelpers.js";
|
|
4
4
|
import { newFakeEmbeddedChartBuilder } from "./fakeembeddedchartbuilder.js";
|
|
5
|
+
import { newFakeContainerInfo } from "./fakecontainerinfo.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @returns {FakeEmbeddedChart}
|
|
@@ -25,13 +26,20 @@ export class FakeEmbeddedChart {
|
|
|
25
26
|
const props = [
|
|
26
27
|
"getAs",
|
|
27
28
|
"getBlob",
|
|
28
|
-
"getContainerInfo",
|
|
29
29
|
];
|
|
30
30
|
props.forEach((f) => {
|
|
31
31
|
this[f] = () => notYetImplemented(f);
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Returns information about where the chart is positioned within a sheet.
|
|
37
|
+
* @returns {FakeContainerInfo}
|
|
38
|
+
*/
|
|
39
|
+
getContainerInfo() {
|
|
40
|
+
return newFakeContainerInfo(this.__apiChart.position?.overlayPosition);
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
/**
|
|
36
44
|
* Returns the ID of this chart.
|
|
37
45
|
* @returns {number}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
InteractiveBrowserCredential,
|
|
3
|
-
ClientSecretCredential
|
|
3
|
+
ClientSecretCredential,
|
|
4
|
+
useIdentityPlugin
|
|
4
5
|
} from "@azure/identity";
|
|
6
|
+
|
|
5
7
|
import { execSync } from 'node:child_process';
|
|
6
8
|
import { readFile, writeFile } from 'fs/promises';
|
|
7
9
|
import { existsSync } from 'fs';
|
|
@@ -40,20 +42,66 @@ function decodeJWT(token) {
|
|
|
40
42
|
return JSON.parse(Buffer.from(encodedPayload, 'base64url').toString('utf-8'));
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
const customCachePlugin = (options) => {
|
|
46
|
+
return {
|
|
47
|
+
beforeCacheAccess: async (cacheContext) => {
|
|
48
|
+
if (existsSync(TOKEN_CACHE_FILE)) {
|
|
49
|
+
try {
|
|
50
|
+
const data = await readFile(TOKEN_CACHE_FILE, 'utf-8');
|
|
51
|
+
let cache;
|
|
52
|
+
if (data.trim().startsWith('{')) {
|
|
53
|
+
cache = JSON.parse(data);
|
|
54
|
+
} else {
|
|
55
|
+
cache = decodeJWT(data.trim());
|
|
56
|
+
}
|
|
57
|
+
if (cache && cache.msalCache) {
|
|
58
|
+
cacheContext.tokenCache.deserialize(cache.msalCache);
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.warn(`...failed to load MS Graph token cache: ${e.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
afterCacheAccess: async (cacheContext) => {
|
|
66
|
+
if (cacheContext.cacheHasChanged) {
|
|
67
|
+
try {
|
|
68
|
+
const msalCache = cacheContext.tokenCache.serialize();
|
|
69
|
+
const cacheData = {
|
|
70
|
+
msalCache,
|
|
71
|
+
token: 'managed-by-msal',
|
|
72
|
+
expiresOn: new Date(Date.now() + 86400000 * 30).getTime() // Keep valid for 30 days
|
|
73
|
+
};
|
|
74
|
+
const jwtString = encodeJWT(cacheData);
|
|
75
|
+
await writeFile(TOKEN_CACHE_FILE, jwtString);
|
|
76
|
+
try {
|
|
77
|
+
chmodSync(TOKEN_CACHE_FILE, 0o600);
|
|
78
|
+
} catch (e) {}
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.warn(`...failed to save MS Graph token cache: ${e.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
useIdentityPlugin((context) => {
|
|
88
|
+
if (context.cachePluginControl) {
|
|
89
|
+
context.cachePluginControl.setPersistence(customCachePlugin);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
43
93
|
async function loadTokenCache() {
|
|
44
94
|
if (existsSync(TOKEN_CACHE_FILE)) {
|
|
45
95
|
try {
|
|
46
96
|
const data = await readFile(TOKEN_CACHE_FILE, 'utf-8');
|
|
47
97
|
|
|
48
98
|
let cache;
|
|
49
|
-
// Handle legacy plain-text JSON format gracefully
|
|
50
99
|
if (data.trim().startsWith('{')) {
|
|
51
100
|
cache = JSON.parse(data);
|
|
52
101
|
} else {
|
|
53
102
|
cache = decodeJWT(data.trim());
|
|
54
103
|
}
|
|
55
104
|
|
|
56
|
-
// Check if expired (buffer of 5 mins)
|
|
57
105
|
if (cache && cache.expiresOn && new Date(cache.expiresOn).getTime() > Date.now() + 300000) {
|
|
58
106
|
return cache.token;
|
|
59
107
|
}
|
|
@@ -70,10 +118,8 @@ async function saveTokenCache(token, expiresOn) {
|
|
|
70
118
|
const jwtString = encodeJWT(cacheData);
|
|
71
119
|
await writeFile(TOKEN_CACHE_FILE, jwtString);
|
|
72
120
|
try {
|
|
73
|
-
chmodSync(TOKEN_CACHE_FILE, 0o600);
|
|
74
|
-
} catch (e) {
|
|
75
|
-
// Ignore if chmod fails (e.g. on non-posix)
|
|
76
|
-
}
|
|
121
|
+
chmodSync(TOKEN_CACHE_FILE, 0o600);
|
|
122
|
+
} catch (e) {}
|
|
77
123
|
} catch (e) {
|
|
78
124
|
console.warn(`...failed to save MS Graph token cache: ${e.message}`);
|
|
79
125
|
}
|
|
@@ -120,7 +166,7 @@ async function isGcpEnv() {
|
|
|
120
166
|
* Gets a Microsoft Graph token.
|
|
121
167
|
*/
|
|
122
168
|
export async function getMsGraphToken(scopes = ['User.Read']) {
|
|
123
|
-
const envTenant = process.env.MS_GRAPH_TENANT_ID || '
|
|
169
|
+
const envTenant = process.env.MS_GRAPH_TENANT_ID || 'consumers';
|
|
124
170
|
const clientId = process.env.MS_GRAPH_CLIENT_ID;
|
|
125
171
|
const clientSecret = process.env.MS_GRAPH_CLIENT_SECRET;
|
|
126
172
|
const msAuthType = process.env.MS_AUTH_TYPE;
|
|
@@ -230,7 +276,11 @@ export async function getMsGraphToken(scopes = ['User.Read']) {
|
|
|
230
276
|
const credentialSilent = new InteractiveBrowserCredential({
|
|
231
277
|
tenantId: (envTenant && envTenant !== 'common') ? envTenant : 'consumers',
|
|
232
278
|
clientId,
|
|
233
|
-
prompt: promptBehavior
|
|
279
|
+
prompt: promptBehavior,
|
|
280
|
+
tokenCachePersistenceOptions: {
|
|
281
|
+
enabled: true,
|
|
282
|
+
name: 'gas-fakes-msgraph-cache'
|
|
283
|
+
}
|
|
234
284
|
});
|
|
235
285
|
|
|
236
286
|
try {
|
|
@@ -244,7 +294,11 @@ export async function getMsGraphToken(scopes = ['User.Read']) {
|
|
|
244
294
|
const credentialInteractive = new InteractiveBrowserCredential({
|
|
245
295
|
tenantId: (envTenant && envTenant !== 'common') ? envTenant : 'consumers',
|
|
246
296
|
clientId,
|
|
247
|
-
prompt: 'select_account consent'
|
|
297
|
+
prompt: 'select_account consent',
|
|
298
|
+
tokenCachePersistenceOptions: {
|
|
299
|
+
enabled: true,
|
|
300
|
+
name: 'gas-fakes-msgraph-cache'
|
|
301
|
+
}
|
|
248
302
|
});
|
|
249
303
|
const tokenResponse = await credentialInteractive.getToken(msScopes);
|
|
250
304
|
syncLog('...retrieved MS Graph token via interactive login');
|
package/exgcp.sh
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# This script reads the GCP_PROJECT_ID from a .env file
|
|
4
|
-
# and exports it as GOOGLE_CLOUD_PROJECT for the current shell session.
|
|
5
|
-
#
|
|
6
|
-
# Usage: source . ./exgcp.sh
|
|
7
|
-
|
|
8
|
-
# Define the path to your .env file relative to the script's location
|
|
9
|
-
ENV_FILE="$(dirname "$0")/.env"
|
|
10
|
-
|
|
11
|
-
# Check if the .env file exists
|
|
12
|
-
|
|
13
|
-
if [ ! -f "$ENV_FILE" ]; then
|
|
14
|
-
echo "Error: .env file not found at path: $ENV_FILE"
|
|
15
|
-
# Use 'return' instead of 'exit' so it doesn't close the user's terminal when sourced
|
|
16
|
-
return 1
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
|
-
# Read the GCP_PROJECT_ID, remove quotes, and handle potential carriage returns
|
|
20
|
-
GOOGLE_CLOUD_PROJECT_VALUE=$(grep -E '^GOOGLE_CLOUD_PROJECT=' "$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')
|
|
23
|
-
OMDB_API_KEY_VALUE=$(grep -E '^OMDB_API_KEY=' "$ENV_FILE" | cut -d '=' -f2 | tr -d '"\r')
|
|
24
|
-
# Check if a value was extracted
|
|
25
|
-
if [ -z "$GOOGLE_CLOUD_PROJECT_VALUE" ]; then
|
|
26
|
-
echo "Error: GOOGLE_CLOUD_PROJECT not found or is empty in $ENV_FILE."
|
|
27
|
-
return 1
|
|
28
|
-
fi
|
|
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 "OMDB_API_KEY_VALUE" ]; then
|
|
38
|
-
echo "OMDB_API_KEY not found or is empty in $ENV_FILE."
|
|
39
|
-
else
|
|
40
|
-
echo "exported: OMDB_API_KEY"
|
|
41
|
-
export OMDB_API_KEY="$OMDB_API_KEY_VALUE"
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
|
-
if [ -z "GEMINI_MODEL_VALUE" ]; then
|
|
45
|
-
echo "GEMINI_MODEL not found or is empty in $ENV_FILE."
|
|
46
|
-
else
|
|
47
|
-
echo "exported: GEMINI_MODEL=$GEMINI_MODEL_VALUE"
|
|
48
|
-
export GEMINI_MODEL="$GEMINI_MODEL_VALUE"
|
|
49
|
-
fi
|
|
50
|
-
|
|
51
|
-
# Export the variable for the current session
|
|
52
|
-
export GOOGLE_CLOUD_PROJECT="$GOOGLE_CLOUD_PROJECT_VALUE"
|
|
53
|
-
|
|
54
|
-
echo "exported: GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT"
|
package/introvideo.png
DELETED
|
Binary file
|
package/logo.png
DELETED
|
Binary file
|
package/plain-logo.png
DELETED
|
Binary file
|
package/testlib.sh
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
gas-fakes -l bmFiddler@13EWG4-lPrEf34itxQhAQ7b9JEbmCBfO8uE4Mhr99CHi3Pw65oxXtq-rU \
|
|
2
|
-
-s "const sheet=SpreadsheetApp.openById('1h9IGIShgVBVUrUjjawk5MaCEQte_7t32XeEP1Z5jXKQ').getSheets()[0];const fiddler = new bmFiddler.Fiddler(sheet);console.log (fiddler.getData().slice(0, 5));"
|