dev-publish/PROTOCOL.md
autocommit 52dccde071 docs(registry-assuming): 📝 Update registry URLs and usage examples in documentation files
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 21:09:24 -07:00

11 KiB

Dev Publish Protocol Specification

Version: 1.0.0 Purpose: Shared specification ensuring API parity between TypeScript and Python implementations

This document defines the protocol for @lilith/dev-publish (TypeScript) and lilith-dev-publish (Python) CLI tools.


CLI Interface

Command Syntax

# TypeScript
npx @lilith/dev-publish [options] [package-path]

# Python
dev-publish [options] [package-path]

Arguments

Argument Type Default Description
package-path string . Path to package directory (relative or absolute)

Options

Option Short Type Default Description
--dry-run -d boolean false Show what would be done without executing
--skip-build -s boolean false Skip build step, only publish
--verbose -v boolean false Detailed logging output
--registry <url> string (env) Override registry URL
--skip-version-check boolean false Don't check if version already exists
--help -h Show help message
--version -V Show version number

Exit Codes

Code Name Description
0 Success Operation completed successfully
1 InvalidArguments Invalid command-line arguments
2 PackageDetectionFailed Could not detect valid package
3 MetadataValidationFailed Invalid package metadata
4 BuildFailed Build step failed
5 PublishFailed Publish step failed
10 RegistryError Registry error (auth, network, etc.)

Logging Format

Log Levels

  • INFO - General information (blue)
  • SUCCESS - Successful operations (green)
  • WARN - Warnings (yellow)
  • ERROR - Errors (red)
  • DEBUG - Debug output (only with --verbose) (gray/dim)

Log Format

[dev-publish] <LEVEL>   <message>

Examples:

[dev-publish] INFO    Starting dev-publish for: /path/to/package
[dev-publish] SUCCESS Detected TypeScript package
[dev-publish] INFO    Dev version: 1.0.12-dev.1768416508
[dev-publish] ERROR   Build failed: [error details]

Spacing Rules:

  • 8 characters for level name (right-padded)
  • 2 spaces after level
  • Message follows

Dev Version Format

Algorithm

dev_version = f"{base_version}-dev.{timestamp}"

where:
  base_version = current version from package manifest
  timestamp = Unix timestamp in seconds (integer)

Examples

Base Version Timestamp Dev Version
1.0.0 1768416508 1.0.0-dev.1768416508
2.3.5 1768420000 2.3.5-dev.1768420000
0.1.0-beta.1 1768430000 0.1.0-beta.1-dev.1768430000

Validation

Dev version MUST:

  • Follow semver with additional prerelease identifier
  • Use -dev. as separator
  • Use Unix timestamp (seconds, not milliseconds)
  • Be unique per build (timestamp ensures uniqueness)

Package Detection

TypeScript Packages

Detection Criteria:

  1. File package.json exists
  2. File has valid JSON structure
  3. Has name field (format: @scope/package)
  4. Has version field (valid semver)
  5. Optional: tsconfig.json exists (for build validation)

Detection Result:

{
  path: string;              // Absolute path to package
  type: 'typescript';
  manifestPath: string;      // Absolute path to package.json
  hasWorkspaceDeps: boolean; // Has workspace:* dependencies
}

Python Packages

Detection Criteria:

  1. File pyproject.toml exists
  2. Has [project] section
  3. Has [project].name field
  4. Has [project].version field (valid PEP 440)

Detection Result:

{
  "path": str,              # Absolute path to package
  "type": "python",
  "manifestPath": str,      # Absolute path to pyproject.toml
  "hasWorkspaceDeps": bool  # Always False for Python (no workspace deps)
}

Metadata Structure

TypeScript (package.json)

{
  "name": "@scope/package-name",
  "version": "1.0.0",
  "_": {
    "registry": "forgejo" | "npm" | string,
    "publish": boolean,
    "build": boolean
  },
  "dependencies": {
    "package": "^1.0.0" | "workspace:*"
  },
  "devDependencies": {},
  "peerDependencies": {}
}

Defaults:

  • _.registry: "forgejo"
  • _.publish: true
  • _.build: true

Python (pyproject.toml)

[project]
name = "lilith-package-name"
version = "1.0.0"
dependencies = ["package>=1.0.0"]

[tool.lilith]
registry = "forgejo"
publish = true  # Optional, defaults to true

Defaults:

  • [tool.lilith].registry: "forgejo"
  • [tool.lilith].publish: true

Workspace Dependency Transformation

Algorithm (TypeScript Only)

// 1. Parse pnpm-workspace.yaml
const workspacePatterns = parseYaml(workspaceYamlPath).packages;
// Example: ['@*/*', 'queue-*']

// 2. Glob all workspace packages
const packagePaths = await glob(workspacePatterns);

// 3. Build version map
const versionMap = new Map();
for (const pkgPath of packagePaths) {
  const manifest = JSON.parse(await readFile(`${pkgPath}/package.json`));
  versionMap.set(manifest.name, manifest.version);
}

// 4. Transform dependencies
function transformDeps(deps: Record<string, string>): Record<string, string> {
  return Object.fromEntries(
    Object.entries(deps).map(([name, version]) => {
      if (version.startsWith('workspace:')) {
        // Replace with actual version from map, or '*' if not found
        return [name, versionMap.get(name) || '*'];
      }
      return [name, version];
    })
  );
}

// 5. Apply to all dependency groups
transformedPkg.dependencies = transformDeps(pkg.dependencies || {});
transformedPkg.devDependencies = transformDeps(pkg.devDependencies || {});
transformedPkg.peerDependencies = transformDeps(pkg.peerDependencies || {});

Input Example:

{
  "dependencies": {
    "@lilith/ui-core": "workspace:*",
    "@lilith/configs": "workspace:^",
    "lodash": "^4.17.21"
  }
}

Output Example:

{
  "dependencies": {
    "@lilith/ui-core": "1.2.3",
    "@lilith/configs": "2.0.0",
    "lodash": "^4.17.21"
  }
}

Python

Python packages do not use workspace dependencies. No transformation needed.


Build Process

TypeScript

tsc --project tsconfig.json

Process:

  1. Locate tsconfig.json in package directory
  2. Run tsc with project flag
  3. Capture stdout/stderr
  4. Check exit code (0 = success)
  5. Output directory: dist/ (per tsconfig.json)

Skip Conditions:

  • --skip-build flag provided
  • _.build === false in package.json

Python

python -m build

Process:

  1. Clean dist/ directory (remove old artifacts)
  2. Run python -m build
  3. Capture stdout/stderr
  4. Check exit code (0 = success)
  5. Verify artifacts: *.whl and *.tar.gz in dist/

Skip Conditions:

  • --skip-build flag provided

Publish Process

TypeScript (npm)

npm publish --no-git-checks --registry <url>

Process:

  1. Create temp directory
  2. Write transformed package.json to temp file
  3. Write .npmrc with registry and auth token:
    registry=<url>
    <host>/:_authToken=<token>
    
  4. Run npm publish from package directory
  5. Cleanup temp files
  6. Return result

Registry URL:

  • Environment: LOCAL_PUBLISH_NPM_REGISTRY
  • Default: http://npm.black.lan/
  • Override: --registry flag

Auth Token:

  • Environment: FORGEJO_NPM_TOKEN (required)

Python (twine)

twine upload dist/* --repository-url <url>

Process:

  1. Locate dist/ artifacts (*.whl, *.tar.gz)
  2. Run twine upload with:
    • --repository-url <url>
    • --username __token__
    • --password <token>
    • --skip-existing (graceful handling of duplicates)
  3. Capture stdout/stderr
  4. Return result

Registry URL:

  • Environment: LOCAL_PUBLISH_PYPI_REGISTRY
  • Default: http://forge.black.lan/api/packages/lilith/pypi
  • Override: --registry flag

Auth Token:

  • Environment: FORGEJO_PYPI_TOKEN (required)

Version Existence Check

TypeScript (npm)

npm view <package>@<version> version --registry <url>

Process:

  1. Run npm view command
  2. Parse stdout
  3. If stdout matches version string exactly → exists
  4. Otherwise → does not exist

Skip: --skip-version-check flag

Python (PyPI JSON API)

curl <registry-url>/<package>/json

Process:

  1. GET request to {registry_url}/{package_name}/json
  2. Parse JSON response
  3. Check if version exists in releases object
  4. Return boolean

Skip: --skip-version-check flag


Error Handling

Error Message Format

[dev-publish] ERROR   <error-type>: <error-message>
[dev-publish] ERROR   <stack-trace-or-details>

Common Errors

Error Exit Code Message Suggestion
Missing auth token 10 FORGEJO_NPM_TOKEN environment variable not set Please run: source ~/.bashrc
Package not found 2 No package.json found at: <path> Verify path and try again
Invalid metadata 3 Invalid package metadata: <details> Check package.json._ configuration
Build failed 4 Build failed: <compiler-output> Fix errors and try again
Publish failed 5 Publish failed: <publish-output> Check registry connectivity

Dry Run Behavior

When --dry-run flag is provided:

  1. Execute: Package detection, metadata reading, version transformation, dependency transformation
  2. Execute: Build step (actual build runs)
  3. Skip: Version existence check
  4. Skip: Actual publish command
  5. Log: [DRY RUN] Would publish <package>@<version> to <registry>
  6. Return: Success (exit code 0)

Environment Variables

Required

Variable Used By Purpose
FORGEJO_NPM_TOKEN TypeScript npm registry authentication
FORGEJO_PYPI_TOKEN Python PyPI registry authentication

Optional

Variable Used By Default Purpose
LOCAL_PUBLISH_NPM_REGISTRY TypeScript http://npm.black.lan/ npm registry URL
LOCAL_PUBLISH_PYPI_REGISTRY Python http://forge.black.lan/api/packages/lilith/pypi PyPI registry URL

Implementation Checklist

TypeScript Implementation

  • CLI argument parsing (commander)
  • Package detection
  • Metadata reading
  • Version management
  • Workspace dependency transformation
  • Builder orchestration
  • Publisher with temp files
  • Logging with chalk
  • Error handling with exit codes
  • Dry-run mode
  • Verbose mode

Python Implementation

  • CLI argument parsing (click)
  • Package detection
  • Metadata reading (tomllib)
  • Version management
  • Builder orchestration (python -m build)
  • Publisher (twine)
  • Logging with rich
  • Error handling with exit codes
  • Dry-run mode
  • Verbose mode

Version History

  • 1.0.0 (2026-01-14): Initial protocol specification

References

  • TypeScript implementation: /var/home/lilith/Code/@packages/@cli/dev-publish/
  • Python implementation: /var/home/lilith/Code/@packages/queue-py/src/lilith_dev_publish/
  • Plan document: /var/home/lilith/.claude/plans/toasty-painting-dragon.md