Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ on:
- '@magmacomputing/tempo-plugin-snap'
- '@magmacomputing/tempo-plugin-sync'
- '@magmacomputing/tempo-plugin-ticker'
- '@magmacomputing/tempo-plugin-geo'
- 'all'
release:
types: [published]
Expand Down Expand Up @@ -57,7 +58,7 @@ jobs:

# Input validation
case "$TARGET" in
@magmacomputing/tempo|@magmacomputing/tempo-fns|@magmacomputing/tempo-plugin-astro|@magmacomputing/tempo-plugin-celestial|@magmacomputing/tempo-plugin-ai|@magmacomputing/tempo-plugin-batch|@magmacomputing/tempo-plugin-finance|@magmacomputing/tempo-plugin-snap|@magmacomputing/tempo-plugin-sync|@magmacomputing/tempo-plugin-ticker|all)
@magmacomputing/tempo|@magmacomputing/tempo-fns|@magmacomputing/tempo-plugin-astro|@magmacomputing/tempo-plugin-celestial|@magmacomputing/tempo-plugin-ai|@magmacomputing/tempo-plugin-batch|@magmacomputing/tempo-plugin-finance|@magmacomputing/tempo-plugin-snap|@magmacomputing/tempo-plugin-sync|@magmacomputing/tempo-plugin-ticker|@magmacomputing/tempo-plugin-geo|all)
;;
*)
echo "❌ Error: Invalid target '$TARGET'"
Expand Down Expand Up @@ -130,6 +131,7 @@ jobs:
publish_pkg "@magmacomputing/tempo-plugin-snap"
publish_pkg "@magmacomputing/tempo-plugin-sync"
publish_pkg "@magmacomputing/tempo-plugin-ticker"
publish_pkg "@magmacomputing/tempo-plugin-geo"
else
publish_pkg "$TARGET"
fi
29 changes: 22 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tempo-monorepo",
"version": "4.1.0",
"version": "4.1.1",
"private": true,
"engines": {
"node": ">=20.0.0"
Expand Down Expand Up @@ -28,6 +28,10 @@
"version:sync": "tempo-cli version-sync",
"catalog:sync": "tempo-cli catalog-sync",
"providers:sync": "npm run build:library && tempo-cli sync-providers",
"plugins:check-versions": "./packages/plugins/.bin/check-versions.sh",
"plugins:check-diff": "./packages/plugins/.bin/check-branch-diff.sh",
"check:versions": "npm run plugins:check-versions",
"check:diff": "npm run plugins:check-diff",
"repl": "npm run repl --workspace=@magmacomputing/tempo",
"repl:plugins": "tsx --import ./packages/plugins/.bin/temporal-polyfill.mts ./packages/plugins/.bin/repl.mts",
"repl:dist": "npm run repl:dist --workspace=@magmacomputing/tempo",
Expand Down
2 changes: 1 addition & 1 deletion packages/library/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@magmacomputing/library",
"version": "4.1.0",
"version": "4.1.1",
"description": "Shared utility library for Tempo",
"author": "Magma Computing Solutions",
"license": "MIT",
Expand Down
22 changes: 16 additions & 6 deletions packages/library/src/common/runtime/mapper.library.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { CONTEXT, getContext } from '#library/utility.library.js';
import { isNullish, isNumber } from '#library/assertion.library.js';
import { geoLocation } from '#browser/mapper.library.js';
import { serverGeoLocation } from '#server/mapper.library.js';
import { isNullish, isNumber, isString } from '#library/assertion.library.js';

export interface GeoLookupResult {
lat?: number;
Expand Down Expand Up @@ -98,8 +96,16 @@ export const coerceGeo = (input?: any): GeoConfig | undefined => {
if (isNumber(lng)) result.longitude = lng;
if (isNumber(elevation)) result.elevation = elevation;
if (sphere === 'north' || sphere === 'south') result.sphere = sphere;
if (typeof country === 'string') result.country = country;
if (typeof city === 'string') result.city = city;
if (isString(country)) result.country = country;
if (isString(city)) result.city = city;

if (geoObj && typeof geoObj === 'object') {
for (const key of Object.keys(geoObj)) {
if (!['latitude', 'lat', 'longitude', 'lng', 'lon', 'long', 'elevation', 'sphere', 'country', 'city'].includes(key)) {
(result as any)[key] = geoObj[key];
Comment thread
magmacomputing marked this conversation as resolved.
Outdated
Comment thread
magmacomputing marked this conversation as resolved.
}
}
}

return Object.keys(result).length > 0 ? result : undefined;
};
Expand Down Expand Up @@ -139,6 +145,7 @@ export const geoLookup = async (opts: Record<string, any> = {}): Promise<GeoLook

switch (type) {
case CONTEXT.Browser: {
const { geoLocation } = await import('#browser/mapper.library.js');
const res = await geoLocation(opts as any);
if (res.error)
return { error: res.error };
Expand All @@ -153,13 +160,16 @@ export const geoLookup = async (opts: Record<string, any> = {}): Promise<GeoLook
if (stashed && isNumber(stashed.latitude) && isNumber(stashed.longitude))
return { lat: stashed.latitude, lng: stashed.longitude, latitude: stashed.latitude, longitude: stashed.longitude };

const { serverGeoLocation } = await import('#server/mapper.library.js');
return serverGeoLocation(opts as any);
}

case CONTEXT.NodeJS:
case CONTEXT.Deno:
default:
default: {
const { serverGeoLocation } = await import('#server/mapper.library.js');
return serverGeoLocation(opts as any);
}
}
}

Expand Down
8 changes: 6 additions & 2 deletions packages/library/src/common/runtime/request.library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,13 @@ export const fetchRequest = <T>(url: string | URL, init = {} as RequestInit, con

let errorBody: any = null;
try {
const errorText = await res.text();
const errorText = isDefined(config.maxBytes)
? await readBoundedBody(res, config.maxBytes)
: (isFunction(res.text) ? await res.text() : '');
try { errorBody = JSON.parse(errorText); } catch { errorBody = errorText; }
} catch { }
} catch (err) {
if (err instanceof HttpError && err.status === 413) throw err;
}

throw new HttpError(res.status, res.statusText, errorBody); // fetch not successful
})
Expand Down
4 changes: 3 additions & 1 deletion packages/plugins/.bin/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Plugin Support Binaries

This directory (`packages/plugins/bin/`) contains internal support scripts and utilities for developing and testing Tempo plugins within the monorepo.
This directory (`packages/plugins/.bin/`) contains internal support scripts and utilities for developing and testing Tempo plugins within the monorepo.

It includes:
- **REPL Environment (`repl.mts`)**: Scripts to initialize an interactive Node.js REPL session with Tempo and Temporal pre-loaded, making it easy to experiment with plugins from the CLI.
- **Polyfill Setup (`temporal-polyfill.mts`)**: Initialization scripts to ensure the `@js-temporal/polyfill` is correctly loaded into the global scope during testing or REPL sessions, allowing plugins to work with native `Temporal` APIs before they are officially adopted by all runtimes.
- **Catalog Synchronization (`catalog-sync.mjs`)**: A developer utility that scans all local and external plugin `package.json` files and extracts their metadata into a centralized `catalog.json` file. Run via `npm run catalog:sync`.
- **Version Check (`check-versions.sh`)**: Compares published NPM versions against local workspace versions to determine which plugins need to be published or re-published. Run via `npm run check:versions` or `npm run plugins:check-versions`.
- **Branch Diff Check (`check-branch-diff.sh`)**: Compares plugin files on the current branch against `main` (or a specified branch) to verify if modified plugins had their semantic versions bumped. Run via `npm run check:diff` or `npm run plugins:check-diff`.
- **TypeScript Configuration (`tsconfig.json`)**: Specific compiler options for running these support scripts directly via tools like `tsx`.

These files are meant for local monorepo development and testing purposes only. They are not published or distributed with any NPM packages.
21 changes: 13 additions & 8 deletions packages/plugins/.bin/check-versions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,23 @@

set -e

# Resolve repository root path (3 levels up from packages/plugins/.bin)
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"

packages=(
"tempo:packages/tempo/package.json"
"tempo-plugin-ai:packages/plugins/ai/package.json"
"tempo-plugin-astro:packages/plugins/astro/package.json"
"tempo-plugin-batch:packages/plugins/batch/package.json"
"tempo-plugin-finance:packages/plugins/finance/package.json"
"tempo-plugin-snap:packages/plugins/snap/package.json"
"tempo-plugin-sync:packages/plugins/sync/package.json"
)

# Resolve repository root path (3 levels up from packages/plugins/.bin)
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
for plugin_dir in "${REPO_ROOT}/packages/plugins"/*; do
if [ -d "${plugin_dir}" ] && [ -f "${plugin_dir}/package.json" ]; then
is_private=$(node --input-type=module -e "import fs from 'fs'; console.log(JSON.parse(fs.readFileSync(process.argv[2], 'utf8')).private ? 'true' : 'false')" dummy "${plugin_dir}/package.json" 2>/dev/null || echo "false")
if [ "${is_private}" = "true" ]; then
continue
fi
plugin_name=$(basename "${plugin_dir}")
packages+=("tempo-plugin-${plugin_name}:packages/plugins/${plugin_name}/package.json")
fi
done

printf "%-38s | %-16s | %-16s | %-12s\n" "Package Name" "Published (NPM)" "Local Workspace" "Status"
printf "%-38s-+-%-16s-+-%-16s-+-%-12s\n" "--------------------------------------" "----------------" "----------------" "------------"
Expand Down
13 changes: 11 additions & 2 deletions packages/plugins/.setup/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"packageName": "@magmacomputing/tempo-plugin-ai",
"plan": "community",
"status": "active",
"version": "1.1.1"
"version": "1.2.1"
},
{
"id": "ticker",
Expand All @@ -69,7 +69,16 @@
"packageName": "@magmacomputing/tempo-plugin-ticker",
"plan": "community",
"status": "active",
"version": "2.3.0"
"version": "2.3.1"
},
{
"id": "geo",
"name": "Geo Plugin",
"description": "Tempo community plugin for IP geolocation lookup, browser hardware location services, and coordinate resolution.",
"packageName": "@magmacomputing/tempo-plugin-geo",
"plan": "community",
"status": "active",
"version": "0.1.0"
},
{
"id": "_std",
Expand Down
46 changes: 35 additions & 11 deletions packages/plugins/.setup/community-plugin-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This template outlines the standard operating procedure for preparing and publis

Ensure the plugin's `package.json` contains the correct community configuration:

- **Version**: Set to a fresh semantic version (e.g., `"1.0.0"` for the first release).
- **Version**: Set to `"0.1.0"` for the initial bootstrap release (allowing the official `1.0.0` GA release to be published via CI with full Sigstore provenance).
- **License**: Must strictly be `"MIT"`.
- **Type**: Set `"type": "module"`.
- **Files**: Include the published files array:
Expand Down Expand Up @@ -143,25 +143,49 @@ All exported components (functions, interfaces, classes, and types) must be prop
export function myExportedFunction(input: string): string { ... }
```

## 7. Release & CI Configuration (`.github/workflows/publish.yml`)
## 7. Monorepo & CI Configuration

When adding a new plugin to the monorepo, update `.github/workflows/publish.yml` to enable manual `workflow_dispatch` provenance releases:
### A. Update Monorepo Lockfile (`package-lock.json`)

1. **Add to Package Selector**: Add `@magmacomputing/tempo-plugin-[name]` to the `options` array under `inputs.package`.
2. **Add to Bulk Publish**: Add the workspace to the `all` branch in the publishing step:
```bash
npm publish --workspace=@magmacomputing/tempo-plugin-[name] $PROVENANCE_FLAG
```
When adding a new workspace package, you **must** update the root monorepo lockfile so that `npm ci` in CI workflows recognizes the new workspace symlink:
```bash
npm install --package-lock-only
```

### B. Release Workflow Configuration (`.github/workflows/publish.yml`)

Update `.github/workflows/publish.yml` to enable manual `workflow_dispatch` provenance releases:

1. **Add to Package Selector**: Add `@magmacomputing/tempo-plugin-[name]` to the `options` array under `inputs.target`.
2. **Add to Target Validation**: Add `@magmacomputing/tempo-plugin-[name]` to the `case "$TARGET" in` validation pattern.
3. **Add to Bulk Publish**: Add `publish_pkg "@magmacomputing/tempo-plugin-[name]"` to the `if [ "$TARGET" = "all" ]` block.

## 8. NPM Registry Trusted Publisher Configuration (OIDC & Provenance)
## 8. Initial Release & Trusted Publisher Configuration (OIDC & Provenance)

When introducing a new plugin or helper package to the ecosystem, you **must** configure a **Trusted Publisher** on `npmjs.com` to enable CI publishing with cryptographic provenance (`--provenance`):
NPM Trusted Publishing (OIDC) requires that a package **already exists** on the npm registry before its access settings can be configured. Therefore, introducing a new plugin involves a one-time bootstrap step followed by configuring automated CI releases:

### Step 1: Manual Initial Publish (Bootstrap)
Because npm cannot configure Trusted Publishers for non-existent packages, the initial bootstrap release (`v0.1.0`) must be published manually by an authenticated maintainer:
1. Build the plugin and navigate to its workspace directory:
```bash
npm run build --workspace=@magmacomputing/tempo-plugin-[name]
cd packages/plugins/[name]
```
2. Authenticate and publish the initial public version:
```bash
npm login
npm publish --access public
```

### Step 2: Configure NPM Trusted Publisher
Once the package exists on `npmjs.com`, configure GitHub Actions OIDC for all future releases:
1. **Navigate to Package Access**: Go to `https://www.npmjs.com/package/@magmacomputing/tempo-plugin-[name]/access`.
2. **Add Publisher**: Under **Publishing Access** $\rightarrow$ **Trusted Publishers**, click **Add GitHub Actions Publisher**.
3. **Configure Settings**:
- **Organization / Owner**: `magmacomputing`
- **Repository**: `magma`
- **Workflow filename**: `publish.yml`
- **Environment**: *(leave blank unless using environment-gated deployments)*
4. **Why this is mandatory**: The Tempo monorepo uses GitHub Actions OIDC (`id-token: write`) to sign and publish packages with Sigstore provenance. Without an explicit Trusted Publisher binding for each new package on `npmjs.com`, NPM will reject `--provenance` publish attempts with `E404` or `E403` permission errors.

### Step 3: Subsequent Releases via CI (`1.0.0`+)
Once configured, bump the package version to `1.0.0` (or subsequent versions) and trigger `.github/workflows/publish.yml` (`workflow_dispatch` or batch release). The release will be cryptographically signed and published with Sigstore provenance (`--provenance`) without requiring long-lived npm tokens.
8 changes: 8 additions & 0 deletions packages/plugins/ai/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to the `@magmacomputing/tempo-plugin-ai` project will be doc
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.1] - 2026-09-07

### Security & Reliability
- **Self-Contained Network Transport (`fetch.ts`)**:
- Decoupled network request utilities from `@magmacomputing/tempo/library` into a self-contained local transport helper.
- Added chunk-by-chunk stream consumption via `res.body.getReader()` with proactive byte accounting and immediate reader cancellation (`await reader.cancel()`).
- Enforced upfront `Content-Length` checks against `maxBytes` and strictly bounded stream reads to prevent unbounded memory allocation and OS thread starvation.

## [1.2.0] - 2026-09-06

### Added
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/ai/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@magmacomputing/tempo-plugin-ai",
"version": "1.2.0",
"version": "1.2.1",
"description": "Tempo community plugin for LLM-powered natural language parsing.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
Loading
Loading