一键导入
gas-fakes-dev
Develop and implement the 'gas-fakes' project, emulating Google Apps Script (GAS) functionality using Node.js.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Develop and implement the 'gas-fakes' project, emulating Google Apps Script (GAS) functionality using Node.js.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Specialized agent for automating Google Workspace tasks locally using gas-fakes. Generates and executes Google Apps Script code on Node.js.
Guide for creating or contributing modular knowledge to the gf_agent skill. Use this when a user wants to teach gf_agent a new trick or document an Apps Script parity oddity.
| name | gas-fakes-dev |
| description | Develop and implement the 'gas-fakes' project, emulating Google Apps Script (GAS) functionality using Node.js. |
This skill enables the agent to assist in the development of the gas-fakes project. The primary objective is to emulate Google Apps Script (GAS) behavior using Node.js and Google APIs, allowing Apps Script code to run in a local Node.js environment.
Before drafting implementation plans, code, tests, refactoring, debugging, analyzing logs, or drafting documentation, Gemini (the Strategic Planner) must first gather all relevant file context, decompose the request into structured atomic sub-tasks, and call the local model (via the omlx:query_local_model tool with the prompt parameter) to generate precise code implementations, detailed tests, refactored components, bug fixes, or documentation updates.
Before implementing, verify the specifications of the target Google Apps Script classes and methods. Ensure that the functionality you are about to build aligns with real GAS behavior.
[!IMPORTANT] ZERO-TOLERANCE DELEGATION GATE You are FORBIDDEN from using
write_fileorreplaceto implement logic, write tests, perform refactoring, diagnose/fix debug errors, or draft documentation yourself. MANDATORY SEQUENCE:
- Gather context (Research).
- CALL
omlx/query_local_modelwith a comprehensive prompt containing specific constraints.- Review and synthesize the output.
- Apply changes to files (e.g., in
src/,test/, etc.).EXCEPTION: This mandate only applies if the local model is available and its use has not been explicitly forbidden by the user. If unavailable or forbidden, you may proceed with the tasks using your own weights, but you MUST document the reason in your
update_topic.You MUST explicitly state "Delegating logic generation/task to local Gemma model" in your
update_topicsummary when transitioning to execution.
src/ directory.You must verify your implementation by writing and executing test scripts.
test/ directory to prove your implementation works and maintains parity. Never add code without adding tests.curl) MUST include an aggressive timeout (e.g., --max-time 5 or --connect-timeout 2) to prevent processes from hanging and accruing costs in the event of a server deadlock.gasmess/gemini/ directory (e.g., gasmess/gemini/test_something.js). This folder is excluded from version control and prevents cluttering the repository root.test/ directory.Whenever you create a new test script (e.g., test/testnewfeature.js) or significantly modify an existing one, you MUST follow the add-new-test.md workflow exactly:
test/ directory.test/test.js: Add the import statement and the test call within testFakes().test/package.json: Add a corresponding entry to the "scripts" section (e.g., "testnewfeature": "node testnewfeature.js execute")..agents/workflows/ directory (specifically test.md and add-new-test.md) to ensure you are following the latest project-specific procedures.[CRITICAL INSTRUCTION]
The gas-fakes project is complex, and bridging Node.js with GAS involves many hidden constraints, specific architectural patterns, and potential errors. You are required to continuously learn and autonomously evolve this SKILL.
SKILL.md file (by adding, deleting, or modifying content) to document the newly acquired knowledge. If necessary, also create or update sample scripts, helper templates, or explanatory Markdown files in the project.Jdbc.getCloudSqlConnection() for PostgreSQL in Apps Script; it only works for MySQL. You MUST use standard Jdbc.getConnection().https://www.gstatic.com/ipranges/goog.json) in your Cloud SQL instance's Authorized Networks to allow connection.Jdbc.getConnection() has strict requirements for Postgres when hosted on Google Cloud:
ssl=true as a query parameter (e.g., jdbc:postgresql://<IP>:<PORT>/<DB>?ssl=true). The Java driver on Apps Script throws The following connection properties are unsupported: ssl.Jdbc.getConnection("jdbc:postgresql://<IP>:<PORT>/<DB>", rawUser, rawPass).Jdbc.getCloudSqlConnection(url, user, password) for MySQL on Google Cloud using the specific protocol format jdbc:google:mysql://[INSTANCE_CONNECTION_NAME]/[DATABASE_NAME].mysql2 driver cannot directly resolve Google Cloud SQL instance names containing colons (e.g. project:region:instance). To emulate Jdbc.getCloudSqlConnection locally without the Cloud SQL Auth Proxy, you must:
gcloud sql instances describe [INSTANCE_NAME] command to dynamically resolve the instance's public IP address.new URL()), as it will fail. Ensure the string is formatted as [protocol://]user:pass@host/db or parse the parameters manually.JdbcResultSet.getBigDecimal() returns a JdbcBigDecimal object on live GAS. This is a Java proxy that may NOT expose standard Java methods like doubleValue(). To get a numeric value safely across all platforms, use Number(val) or parseFloat(val.toString()).JdbcResultSet methods (e.g., getString()) support both 1-indexed integers and string column labels. Ensure the fake implementation resolves both using findColumn.;) in strings passed to prepareStatement. Some drivers (e.g., MySQL on GAS) may fail to recognize ? placeholders if a semicolon is present.Jdbc.getCloudSqlConnection.Jdbc.getConnection.Jdbc.getConnection.prepareStatement method may fail with Parameter index out of range (1 > 0) because the older GAS driver cannot parse placeholders in modern MySQL 8+ protocols.
try/catch fallback to standard Statement.execute() with manually escaped values if prepareStatement fails for MySQL backends.TextDecoder, TextEncoder, fetch, setTimeout, and ReadableStream.const str = Utilities.newBlob(bytes).getDataAsString();
https://www.gstatic.com/ipranges/goog.json) in your Cloud SQL instance's Authorized Networks to allow connection.if (ScriptApp.isFake).if (ScriptApp.isFake) blocks. Whenever possible, verify behaviors and expected return values using the Public API.
__fakeObjectType === 'JdbcBlob', simply call blob.length() or blob.getBytes(). If these work, the object is implicitly of the correct type.if (ScriptApp.isFake) makes the test suite harder to maintain and reduces confidence that the same test is actually verifying the same behavior on both platforms.getBlob() requires an OID large object column — not BYTEA: Calling rs.getBlob(n) on a TEXT or BYTEA column throws "Bad value for type long : <value>" because the PostgreSQL JDBC driver's getBlob() internally calls getLong() to retrieve a PostgreSQL large object OID from pg_largeobject. For inline binary data, use getBytes() / getBinaryStream() instead. MySQL's BLOB type maps directly and works with getBlob(). In practice: never test getBlob() against a PostgreSQL/CockroachDB backend in a parity test.createStatement() returns FORWARD_ONLY: Plain conn.createStatement() returns a forward-only cursor. Navigation methods last(), first(), absolute(), relative(), previous(), beforeFirst(), afterLast() all throw "Operation requires a scrollable ResultSet". Use conn.createStatement(1004, 1007) (TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY) to get a scrollable cursor. In the fake, these parameters are accepted but advisory — results are already buffered.getFloat() is 32-bit: GAS rs.getFloat() returns an IEEE 754 single-precision float. 1.1 becomes 1.100000023841858. Always compare with Math.fround(expected). The fake applies Math.fround() accordingly.getDouble() is 64-bit: Unlike the above, rs.getDouble() returns a full 64-bit double.[!DANGER] NEVER RUN TESTS FROM THE ROOT DIRECTORY. All test commands MUST be run from within the
test/directory. Failure to do so will result in.envvariables not being loaded and tests failing silently or with obscure errors. CORRECT:cd test && node testsheetsdeveloper.js executeINCORRECT:node test/testsheetsdeveloper.js
test/ directory.node, you MUST append the execute argument (e.g., node testsomething.js execute).node test.js or npm test) unless explicitly requested or necessary for final verification. Instead, run only the specific test file you are working on. This saves significant time and avoids API rate limits.test/package.json resolves "@mcpher/gas-fakes": "file:.." as a local symlink. Because of this, import '@mcpher/gas-fakes' inside the test/ directory automatically pulls in your local, uncommitted changes from src/. NEVER rewrite test imports to relative paths like import '../src/index.js'. Doing so breaks the test suite's isolation from module-level side effects and ignores the intended aliasing architecture.gas-fakes uses a background worker (src/support/workersync/worker.js) to make async API calls synchronous. If an API request fails, the worker catches the error, serializes it (often as a nested JSON string like err.response.data.error), and writes it to a SharedArrayBuffer.
@mcpher/unit's t.threw, you must ensure that the synchronizer.js successfully extracts the underlying error message and explicitly assigns it when re-hydrating the Error object on the main thread (otherwise err.message might be undefined).DeveloperMetadata using the Sheets API (batchUpdate), the API considers the location.locationType property to be read-only. You must supply the boundary object (dimensionRange, spreadsheet, or sheetId), but you MUST NOT supply the string locationType. Including it will cause a 400 Bad Request.file.getAs): Live Apps Script allows developers to call file.getAs('application/pdf') on plain text files. However, the standard Google Drive REST API (drive.files.export) strictly only supports exporting Google Docs Editor files (Docs, Sheets, Slides).
gas-fakes implements a two-step conversion workaround under the hood: when an export fails due to the fileNotExportable limitation, gas-fakes temporarily copies the file into a Google Doc (or Sheet/Slide), exports the temporary file to the requested format, and then trashes the temporary file. This ensures file.getAs('application/pdf') succeeds transparently for text and CSV files just like it does in live Apps Script.DriveApp, functions like getViewers() and getEditors() do NOT cascade or inherit roles. A user is only returned in the specific group they are assigned to via the Drive API.
owner role -> returned by getOwner()writer role -> returned by getEditors()reader and commenter roles -> returned by getViewers()DriveApp (which immediately throws an Invalid argument: id error if passed null or undefined), Live Apps Script Advanced Services (like Drive.Files.get(null)) might silently fail or not throw an easily catchable exception. Do not write parity tests that expect Advanced Services to consistently throw specific errors for null parameters.gas-fakes implementation of GmailMessage (i.e. FakeGmailMessage) does not natively support all methods found in Apps Script (e.g., getSubject(), getDate(), getSnippet(), getFrom(), getTo()).payload.headers array to find the 'Subject' or 'Date').GmailThread.getMessages() is not implemented directly on the FakeGmailThread object. You should use GmailApp.getMessagesForThread(thread) instead.gcloud, Google Cloud IAM may take several seconds to propagate changes. Subsequent commands referencing the new entity may fail with "not found" errors if executed too quickly.gcloud iam commands that depend on recently created resources. A simple busy-wait or execSync to a sleep command can be used in a synchronous CLI environment to ensure portability.while (Date.now() - start < delay)) is often more reliable than depending on OS-specific sleep or timeout commands when running inside a CLI tool.gas-fakes (like FakeAdvGmailMessages) do not automatically expose all methods from the underlying googleapis package. If you need a method like gmail.users.messages.modify or gmail.users.messages.attachments.get, you MUST explicitly define a stub method on the FakeAdvGmailMessages class that invokes this._call().sxGmail): The synchronous worker wrapper (sxGmail in sxgmail.js) was updated to support dot notation. When adding deeply nested API calls (e.g., method: 'attachments.get'), sxGmail can iteratively resolve the nested function context on the googleapis client.FakeGmailMessage or FakeGmailThread (e.g., addLabel(), markRead()) via the REST API does not automatically update the local object's cached labels or headers. To maintain parity with Live GAS ("forces the message to refresh"), modifier methods MUST conclude by calling and returning this.refresh(), which re-fetches the latest state from the API.Gmail.Users.Drafts.create returns a draft resource, the embedded .message object is a minimal stub containing only id and threadId. It lacks the payload and headers. Therefore, FakeGmailDraft.getMessage() MUST perform a distinct Gmail.Users.Messages.get API call using the message ID to retrieve the fully hydrated message (otherwise getSubject(), getBody(), etc., will be empty).sheetsCacher), it may be returned as a reference. If the calling code mutates this data (e.g., using Array.prototype.shift() on results from getValues()), the mutation affects the cache itself. Subsequent calls for the same data will then return the mutated (incorrect) results.Syncit.js, ensure that both getEntry results and data being passed to setEntry are deep-copied (e.g., using JSON.parse(JSON.stringify(data))). This ensures that the cache remains an immutable snapshot of the API response and that each caller receives a fresh, independent copy.fxGeneric and fxDriveGet functions in Syncit.js have been updated to use normalizeSerialization() for this purpose.Drive.Files.list), the API returns plain JSON objects. If you convert an entire chunk of these objects into class instances (e.g., FakeDriveFolder) prematurely and store them in the state (or tank), and then subsequently pass those instances back through the instantiation function (__settleClass), you will double-wrap them. This causes the internal this.meta to be a Fake instance instead of a plain object.improveFileCache mechanism (since the cached "raw" data is actually an instance). Later API calls that rely on reading this.meta (e.g., checking Reflect.has(this.meta, 'id')) fail because normalizeSerialization strips the prototype properties, resulting in a plain object missing its core fields. This leads to infinite resolution loops (e.g., RangeError: Maximum call stack size exceeded in getId()).filesink() in driveiterators.js) MUST store only plain API JSON objects in their state (tank). Only convert the raw API object into a Fake instance at the exact moment it is yielded or returned to the caller.gas-fakes using the Drive.Permissions endpoint) strictly isolates users by their specific role (e.g., writer, reader). However, Live Apps Script behaves differently: getViewers() returns anyone with at least view access (including Editors and the Owner), and getEditors() returns anyone with at least edit access (including the Owner).getSharers() helper in filesharers.js dynamically expands the queried roles based on this hierarchy to match live GAS behavior. Specifically:
writer, we also fetch owner.reader or commenter, we fetch writer and owner as well.colorScheme property is only present on Master pages. Slide and Layout pages inherit their color scheme from their parent master. gas-fakes emulates this by resolving the master page when getColorScheme() is called on a slide or layout.ColorScheme via the API (updatePageProperties), you MUST provide the entire array of 12 theme color pairs (Dark 1, Light 1, etc.). Providing only the updated color will result in an API error.OpaqueColor specifies a nested rgbColor object, ColorScheme results (and updates) often use a flat structure where red, green, and blue are direct properties of the color object. gas-fakes handles both formats to ensure compatibility.layoutProperties.masterObjectId, not at the top level of the page resource.solidFill object even when propertyState is 'NOT_RENDERED' (representing transparent/none). When implementing getType() or isVisible(), you must verify that propertyState !== 'NOT_RENDERED' to correctly identify transparent fills.SKILL.md when new knowledge is extracted and crystallized.progress/ directory (e.g., progress/spreadsheet.md) and the top-level progress.md are generated automatically.progress/ documentation. This acts as your "Definition of Done".
npm run docs
(This command executes the full documentation pipeline including gi-analyzer-all.js, gi-render.js, and gi-progress-summary.js.)doccreation/gi-analyzer-all.js) detects implemented methods by searching for method names in the src/ directory.
classSynonyms object within doccreation/gi-analyzer-all.js.require* in DataValidationBuilder or set*/get* in Range), the analyzer must be explicitly updated to map these methods from their source definitions (e.g., datavalidationcriteriamapping.js or sheetrangemakers.js) to the target class.notYetImplemented() are automatically marked as in progress. Once the implementation is complete and the call to notYetImplemented() is removed, the status will switch to completed upon running the pipeline.When implementing or modifying EmbeddedChartBuilder features, you must handle the strict differences between Google's Java V8 engine and the actual Sheets REST API payloads:
Charts Enums (e.g., passing "SHOW_ALL" instead of Charts.ChartHiddenDimensionStrategy.SHOW_ALL throws The parameters (String) don't match the method signature). All new Enums MUST be mapped in src/services/enums/ and exposed via the parent FakeApp proxy.EmbeddedChartBuilder does not match the Google Sheets REST API.
BOTTOM becomes BOTTOM_LEGEND.PIE and HISTOGRAM charts cannot be sent in a basicChart spec wrapper; they require top-level pieChart and histogramChart payloads.COMBO charts will crash the Sheets API if their internal series arrays are missing explicit type definitions (COLUMN or LINE). The visual default fallback in Apps Script is COLUMN..build() translation layer, or within dedicated mappers like chartenummapping.js.toString() output changes dynamically depending on the active internal chart type (e.g., com.google.apps.maestro... vs EmbeddedPieChartBuilder). Use FakeEmbeddedChartBuilder's dynamic toString() pattern to maintain parity.setTitle, setBackgroundColor) on the root EmbeddedChartBuilder; they are only available after casting (.asPieChart()). Furthermore, setHiddenDimensionStrategy will throw a backend Unexpected error if called before assigning a type, or if called on an incompatible type (like a Pie chart).new (e.g. new FakeElement(), new FakeAttribute(), etc.). These classes do not exist in the live Google Apps Script environment, where the same tests are run. Instead, always instantiate objects via the public factory methods of their respective service (e.g., XmlService.createElement()) and assert against properties instead of direct object reference comparisons.gas-fakes structural properties (e.g., checking builder.__apiChart.spec.title). The test suite executes against both local Node.js mocks and Live Apps Script Java classes. Live Apps Script does not expose these internal variables, causing tests to crash.__behavior): Objects like ScriptApp.__behavior or sandboxMode exist ONLY in the local Node.js environment. If you need to manipulate the sandbox during a test (e.g., adding a whitelist ID), you MUST wrap that code in an if (ScriptApp.isFake) block. Attempting to access ScriptApp.__behavior.sandboxMode in Live Apps Script will result in a TypeError: Cannot read properties of undefined.ScriptApp.isFake for private implementation assertions unless strictly manipulating the local test harness (like the sandbox).gas-fakes, templates (<?!= Include.html() ?>) and client-side RPC calls (google.script.run.foo()) require backend functions/variables to be explicitly exported from the entry point (index.js).export or import statements. Including them will cause syntax errors in the Apps Script environment.gas-fakes togas CLI command to prepare your local code for deployment.
togas command automatically comments out import statements and removes export keywords.init or --target).clasp push to deploy the transformed code to Apps Script.For rapid and accurate verification of Apps Script class and method signatures, parameter lists, return types, and descriptions, developers should prioritize searching the local documentation repository.
The file /Users/brucemcpherson/Documents/repos/gas-fakes/doccreation/gi-fake-all.json contains a complete, comprehensive duplicate of all Apps Script documentation.
Prioritization Strategy:
Instead of relying on external web searches, which can be slow, prone to outdated information, or lack precise syntax matching, use local command-line tools (grep, jq, etc.) to query gi-fake-all.json.
Why Local Search is Superior: