opencode-synced 0.2.0 → 0.4.0
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/dist/command/sync-enable-secrets.md +6 -0
- package/dist/command/sync-init.md +11 -0
- package/dist/command/sync-link.md +15 -0
- package/dist/command/sync-pull.md +6 -0
- package/dist/command/sync-push.md +5 -0
- package/dist/command/sync-resolve.md +5 -0
- package/dist/command/sync-status.md +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +28 -4
- package/dist/sync/apply.d.ts +3 -0
- package/dist/sync/commit.d.ts +9 -0
- package/dist/sync/config.d.ts +35 -0
- package/dist/sync/config.test.d.ts +1 -0
- package/dist/sync/errors.d.ts +19 -0
- package/dist/sync/paths.d.ts +47 -0
- package/dist/sync/paths.test.d.ts +1 -0
- package/dist/sync/repo.d.ts +31 -0
- package/dist/sync/repo.test.d.ts +1 -0
- package/dist/sync/service.d.ts +31 -0
- package/dist/sync/utils.d.ts +17 -0
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Initialize opencode-synced configuration
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Use the opencode_sync tool with command "init".
|
|
6
|
+
The repo will be created automatically if it doesn't exist (private by default).
|
|
7
|
+
Default repo name is "my-opencode-config" with owner auto-detected from GitHub CLI.
|
|
8
|
+
If the user wants a custom repo name, pass name="custom-name".
|
|
9
|
+
If the user wants an org-owned repo, pass owner="org-name".
|
|
10
|
+
If the user wants a public repo, pass private=false.
|
|
11
|
+
Include includeSecrets if the user explicitly opts in.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Link this computer to an existing sync repo
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Use the opencode_sync tool with command "link".
|
|
6
|
+
This command is for linking a second (or additional) computer to an existing sync repo that was created on another machine.
|
|
7
|
+
|
|
8
|
+
IMPORTANT: This will OVERWRITE the local OpenCode configuration with the contents from the synced repo. The only thing preserved is the local overrides file (opencode-synced.overrides.jsonc).
|
|
9
|
+
|
|
10
|
+
If the user provides a repo name argument, pass it as name="repo-name".
|
|
11
|
+
If no repo name is provided, the tool will automatically search for common sync repo names.
|
|
12
|
+
|
|
13
|
+
After linking:
|
|
14
|
+
- Remind the user to restart OpenCode to apply the synced config
|
|
15
|
+
- If they want to enable secrets sync, they should run /sync-enable-secrets
|
package/dist/index.d.ts
ADDED
package/dist/index.js
CHANGED
|
@@ -11,7 +11,9 @@ var __export = (target, all) => {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// src/index.ts
|
|
14
|
+
import fs4 from "fs/promises";
|
|
14
15
|
import path5 from "path";
|
|
16
|
+
import { fileURLToPath } from "url";
|
|
15
17
|
|
|
16
18
|
// node_modules/zod/v4/classic/external.js
|
|
17
19
|
var exports_external = {};
|
|
@@ -13746,12 +13748,34 @@ function parseFrontmatter(content) {
|
|
|
13746
13748
|
}
|
|
13747
13749
|
return { frontmatter, body: body.trim() };
|
|
13748
13750
|
}
|
|
13751
|
+
function getModuleDir() {
|
|
13752
|
+
if (typeof import.meta.dir === "string") {
|
|
13753
|
+
return import.meta.dir;
|
|
13754
|
+
}
|
|
13755
|
+
return path5.dirname(fileURLToPath(import.meta.url));
|
|
13756
|
+
}
|
|
13757
|
+
async function scanMdFiles(dir) {
|
|
13758
|
+
const files = [];
|
|
13759
|
+
async function walk(currentDir) {
|
|
13760
|
+
const entries = await fs4.readdir(currentDir, { withFileTypes: true });
|
|
13761
|
+
for (const entry of entries) {
|
|
13762
|
+
const fullPath = path5.join(currentDir, entry.name);
|
|
13763
|
+
if (entry.isDirectory()) {
|
|
13764
|
+
await walk(fullPath);
|
|
13765
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
13766
|
+
files.push(fullPath);
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13769
|
+
}
|
|
13770
|
+
await walk(dir);
|
|
13771
|
+
return files;
|
|
13772
|
+
}
|
|
13749
13773
|
async function loadCommands() {
|
|
13750
13774
|
const commands = [];
|
|
13751
|
-
const commandDir = path5.join(
|
|
13752
|
-
const
|
|
13753
|
-
for
|
|
13754
|
-
const content = await
|
|
13775
|
+
const commandDir = path5.join(getModuleDir(), "command");
|
|
13776
|
+
const files = await scanMdFiles(commandDir);
|
|
13777
|
+
for (const file2 of files) {
|
|
13778
|
+
const content = await fs4.readFile(file2, "utf-8");
|
|
13755
13779
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
13756
13780
|
const relativePath = path5.relative(commandDir, file2);
|
|
13757
13781
|
const name = relativePath.replace(/\.md$/, "").replace(/\//g, "-");
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SyncPlan } from './paths.ts';
|
|
2
|
+
export declare function syncRepoToLocal(plan: SyncPlan, overrides: Record<string, unknown> | null): Promise<void>;
|
|
3
|
+
export declare function syncLocalToRepo(plan: SyncPlan, overrides: Record<string, unknown> | null): Promise<void>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PluginInput } from '@opencode-ai/plugin';
|
|
2
|
+
type CommitClient = PluginInput['client'];
|
|
3
|
+
type Shell = PluginInput['$'];
|
|
4
|
+
interface CommitContext {
|
|
5
|
+
client: CommitClient;
|
|
6
|
+
$: Shell;
|
|
7
|
+
}
|
|
8
|
+
export declare function generateCommitMessage(ctx: CommitContext, repoDir: string, fallbackDate?: Date): Promise<string>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SyncLocations } from './paths.ts';
|
|
2
|
+
export interface SyncRepoConfig {
|
|
3
|
+
url?: string;
|
|
4
|
+
owner?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
branch?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SyncConfig {
|
|
9
|
+
repo?: SyncRepoConfig;
|
|
10
|
+
localRepoPath?: string;
|
|
11
|
+
includeSecrets?: boolean;
|
|
12
|
+
includeSessions?: boolean;
|
|
13
|
+
includePromptStash?: boolean;
|
|
14
|
+
extraSecretPaths?: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface SyncState {
|
|
17
|
+
lastPull?: string;
|
|
18
|
+
lastPush?: string;
|
|
19
|
+
lastRemoteUpdate?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function pathExists(filePath: string): Promise<boolean>;
|
|
22
|
+
export declare function normalizeSyncConfig(config: SyncConfig): SyncConfig;
|
|
23
|
+
export declare function loadSyncConfig(locations: SyncLocations): Promise<SyncConfig | null>;
|
|
24
|
+
export declare function writeSyncConfig(locations: SyncLocations, config: SyncConfig): Promise<void>;
|
|
25
|
+
export declare function loadOverrides(locations: SyncLocations): Promise<Record<string, unknown> | null>;
|
|
26
|
+
export declare function loadState(locations: SyncLocations): Promise<SyncState>;
|
|
27
|
+
export declare function writeState(locations: SyncLocations, state: SyncState): Promise<void>;
|
|
28
|
+
export declare function applyOverridesToRuntimeConfig(config: Record<string, unknown>, overrides: Record<string, unknown>): void;
|
|
29
|
+
export declare function deepMerge<T>(base: T, override: unknown): T;
|
|
30
|
+
export declare function stripOverrides(localConfig: Record<string, unknown>, overrides: Record<string, unknown>, baseConfig: Record<string, unknown> | null): Record<string, unknown>;
|
|
31
|
+
export declare function parseJsonc<T>(content: string): T;
|
|
32
|
+
export declare function writeJsonFile(filePath: string, data: unknown, options?: {
|
|
33
|
+
jsonc: boolean;
|
|
34
|
+
mode?: number;
|
|
35
|
+
}): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class SyncError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
constructor(code: string, message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class SyncConfigMissingError extends SyncError {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class RepoDivergedError extends SyncError {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class RepoPrivateRequiredError extends SyncError {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class RepoVisibilityError extends SyncError {
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
export declare class SyncCommandError extends SyncError {
|
|
18
|
+
constructor(message: string);
|
|
19
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { SyncConfig } from './config.ts';
|
|
2
|
+
export interface XdgPaths {
|
|
3
|
+
homeDir: string;
|
|
4
|
+
configDir: string;
|
|
5
|
+
dataDir: string;
|
|
6
|
+
stateDir: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SyncLocations {
|
|
9
|
+
xdg: XdgPaths;
|
|
10
|
+
configRoot: string;
|
|
11
|
+
syncConfigPath: string;
|
|
12
|
+
overridesPath: string;
|
|
13
|
+
statePath: string;
|
|
14
|
+
defaultRepoDir: string;
|
|
15
|
+
}
|
|
16
|
+
export type SyncItemType = 'file' | 'dir';
|
|
17
|
+
export interface SyncItem {
|
|
18
|
+
localPath: string;
|
|
19
|
+
repoPath: string;
|
|
20
|
+
type: SyncItemType;
|
|
21
|
+
isSecret: boolean;
|
|
22
|
+
isConfigFile: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface ExtraSecretPlan {
|
|
25
|
+
allowlist: string[];
|
|
26
|
+
manifestPath: string;
|
|
27
|
+
entries: Array<{
|
|
28
|
+
sourcePath: string;
|
|
29
|
+
repoPath: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
export interface SyncPlan {
|
|
33
|
+
items: SyncItem[];
|
|
34
|
+
extraSecrets: ExtraSecretPlan;
|
|
35
|
+
repoRoot: string;
|
|
36
|
+
homeDir: string;
|
|
37
|
+
platform: NodeJS.Platform;
|
|
38
|
+
}
|
|
39
|
+
export declare function resolveHomeDir(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string;
|
|
40
|
+
export declare function resolveXdgPaths(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): XdgPaths;
|
|
41
|
+
export declare function resolveSyncLocations(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): SyncLocations;
|
|
42
|
+
export declare function expandHome(inputPath: string, homeDir: string): string;
|
|
43
|
+
export declare function normalizePath(inputPath: string, homeDir: string, platform?: NodeJS.Platform): string;
|
|
44
|
+
export declare function isSamePath(left: string, right: string, homeDir: string, platform?: NodeJS.Platform): boolean;
|
|
45
|
+
export declare function encodeSecretPath(inputPath: string): string;
|
|
46
|
+
export declare function resolveRepoRoot(config: SyncConfig | null, locations: SyncLocations): string;
|
|
47
|
+
export declare function buildSyncPlan(config: SyncConfig, locations: SyncLocations, repoRoot: string, platform?: NodeJS.Platform): SyncPlan;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PluginInput } from '@opencode-ai/plugin';
|
|
2
|
+
import type { SyncConfig } from './config.ts';
|
|
3
|
+
export interface RepoStatus {
|
|
4
|
+
branch: string;
|
|
5
|
+
changes: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface RepoUpdateResult {
|
|
8
|
+
updated: boolean;
|
|
9
|
+
branch: string;
|
|
10
|
+
}
|
|
11
|
+
type Shell = PluginInput['$'];
|
|
12
|
+
export declare function isRepoCloned(repoDir: string): Promise<boolean>;
|
|
13
|
+
export declare function resolveRepoIdentifier(config: SyncConfig): string;
|
|
14
|
+
export declare function resolveRepoBranch(config: SyncConfig, fallback?: string): string;
|
|
15
|
+
export declare function ensureRepoCloned($: Shell, config: SyncConfig, repoDir: string): Promise<void>;
|
|
16
|
+
export declare function ensureRepoPrivate($: Shell, config: SyncConfig): Promise<void>;
|
|
17
|
+
export declare function parseRepoVisibility(output: string): boolean;
|
|
18
|
+
export declare function fetchAndFastForward($: Shell, repoDir: string, branch: string): Promise<RepoUpdateResult>;
|
|
19
|
+
export declare function getRepoStatus($: Shell, repoDir: string): Promise<RepoStatus>;
|
|
20
|
+
export declare function hasLocalChanges($: Shell, repoDir: string): Promise<boolean>;
|
|
21
|
+
export declare function commitAll($: Shell, repoDir: string, message: string): Promise<void>;
|
|
22
|
+
export declare function pushBranch($: Shell, repoDir: string, branch: string): Promise<void>;
|
|
23
|
+
export declare function repoExists($: Shell, repoIdentifier: string): Promise<boolean>;
|
|
24
|
+
export declare function getAuthenticatedUser($: Shell): Promise<string>;
|
|
25
|
+
export interface FoundRepo {
|
|
26
|
+
owner: string;
|
|
27
|
+
name: string;
|
|
28
|
+
isPrivate: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function findSyncRepo($: Shell, repoName?: string): Promise<FoundRepo | null>;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PluginInput } from '@opencode-ai/plugin';
|
|
2
|
+
type SyncServiceContext = Pick<PluginInput, 'client' | '$'>;
|
|
3
|
+
interface InitOptions {
|
|
4
|
+
repo?: string;
|
|
5
|
+
owner?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
url?: string;
|
|
8
|
+
branch?: string;
|
|
9
|
+
includeSecrets?: boolean;
|
|
10
|
+
includeSessions?: boolean;
|
|
11
|
+
includePromptStash?: boolean;
|
|
12
|
+
create?: boolean;
|
|
13
|
+
private?: boolean;
|
|
14
|
+
extraSecretPaths?: string[];
|
|
15
|
+
localRepoPath?: string;
|
|
16
|
+
}
|
|
17
|
+
interface LinkOptions {
|
|
18
|
+
repo?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface SyncService {
|
|
21
|
+
startupSync: () => Promise<void>;
|
|
22
|
+
status: () => Promise<string>;
|
|
23
|
+
init: (_options: InitOptions) => Promise<string>;
|
|
24
|
+
link: (_options: LinkOptions) => Promise<string>;
|
|
25
|
+
pull: () => Promise<string>;
|
|
26
|
+
push: () => Promise<string>;
|
|
27
|
+
enableSecrets: (_extraSecretPaths?: string[]) => Promise<string>;
|
|
28
|
+
resolve: () => Promise<string>;
|
|
29
|
+
}
|
|
30
|
+
export declare function createSyncService(ctx: SyncServiceContext): SyncService;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PluginInput } from '@opencode-ai/plugin';
|
|
2
|
+
type Client = PluginInput['client'];
|
|
3
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
4
|
+
export declare function createLogger(client: Client): {
|
|
5
|
+
debug: (message: string, extra?: Record<string, unknown>) => void;
|
|
6
|
+
info: (message: string, extra?: Record<string, unknown>) => void;
|
|
7
|
+
warn: (message: string, extra?: Record<string, unknown>) => void;
|
|
8
|
+
error: (message: string, extra?: Record<string, unknown>) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function showToast(client: Client, message: string, variant: 'info' | 'success' | 'warning' | 'error'): Promise<void>;
|
|
11
|
+
export declare function unwrapData<T>(response: unknown): T | null;
|
|
12
|
+
export declare function extractTextFromResponse(response: unknown): string | null;
|
|
13
|
+
export declare function resolveSmallModel(client: Client): Promise<{
|
|
14
|
+
providerID: string;
|
|
15
|
+
modelID: string;
|
|
16
|
+
} | null>;
|
|
17
|
+
export {};
|