@dcl-regenesislabs/opendcl 0.1.0-22234509684.commit-63dfd19 → 0.1.0-22239132687.commit-eccf1dd
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 +22 -3
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/extensions/permissions/index.ts +77 -0
- package/extensions/permissions/utils.ts +82 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,9 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/dcl-regenesislabs/opendcl/actions/workflows/ci.yaml)
|
|
4
4
|
|
|
5
|
-
AI
|
|
5
|
+
Build Decentraland scenes with AI. Describe what you want in plain language — OpenDCL handles the SDK7 code, ECS architecture, and deployment for you.
|
|
6
6
|
|
|
7
|
-
OpenDCL is a terminal-based AI agent that
|
|
7
|
+
OpenDCL is a terminal-based AI agent that knows Decentraland inside out: every component, every pattern, every constraint. Whether you're a first-time creator or a seasoned developer, you go from idea to running scene in minutes instead of hours.
|
|
8
|
+
|
|
9
|
+
## Why OpenDCL?
|
|
10
|
+
|
|
11
|
+
Building a Decentraland scene today means learning SDK7, an entity-component-system architecture, TypeScript, 60+ ECS components, parcel size limits, a QuickJS sandbox with no Node.js APIs, and a deployment pipeline. That's a steep learning curve — and it slows down even experienced developers on repetitive tasks.
|
|
12
|
+
|
|
13
|
+
OpenDCL puts an AI assistant in your terminal that already knows all of this. You describe what you want, and it writes correct, deployable scene code. It validates TypeScript as it goes, knows which 3D assets are freely available, and handles multiplayer sync, UI, animations, and deployment config without you needing to look anything up.
|
|
14
|
+
|
|
15
|
+
The result: **more creators building more scenes, faster.**
|
|
16
|
+
|
|
17
|
+
## Use Cases
|
|
18
|
+
|
|
19
|
+
- **Beginners & non-developers** — "Build me a gallery with clickable paintings." Go from zero to a working scene without writing code manually.
|
|
20
|
+
- **Experienced developers** — Skip the boilerplate. Let the agent handle multiplayer sync, UI scaffolding, and deployment config while you focus on creative decisions.
|
|
21
|
+
- **Teams & studios** — Prototype scene concepts quickly and iterate before committing full dev resources.
|
|
22
|
+
- **Land owners & brands** — Build and update experiences on your LAND without hiring a dedicated development team.
|
|
23
|
+
- **Event organizers** — Spin up interactive stages, gathering areas, and event spaces fast.
|
|
24
|
+
- **Hackathons & game jams** — Go from concept to deployed scene in hours instead of days.
|
|
8
25
|
|
|
9
26
|
## Features
|
|
10
27
|
|
|
@@ -15,6 +32,7 @@ OpenDCL is a terminal-based AI agent that understands Decentraland's SDK, compon
|
|
|
15
32
|
- **Integrated commands** — `/init` to scaffold, `/preview` to launch the dev server, `/tasks` to manage running processes, `/review` to audit code
|
|
16
33
|
- **TypeScript validation** — catches type errors immediately after writing code
|
|
17
34
|
- **Free 3D asset catalog** — 900+ CC0-licensed models the agent can suggest and help you use
|
|
35
|
+
- **Permission gate** — prompts for confirmation before destructive bash commands or writes to sensitive files
|
|
18
36
|
- **Session persistence** — pick up where you left off across sessions
|
|
19
37
|
|
|
20
38
|
## Quick Start
|
|
@@ -137,7 +155,8 @@ opendcl/
|
|
|
137
155
|
│ ├── dcl-update-check.ts # Checks npm for newer OpenDCL versions
|
|
138
156
|
│ ├── dcl-validate.ts # Post-write TypeScript validation
|
|
139
157
|
│ ├── dcl-tasks.ts # /tasks command (process manager)
|
|
140
|
-
│
|
|
158
|
+
│ ├── process-registry.ts # Shared background process registry
|
|
159
|
+
│ └── permissions/ # Permission gate for dangerous operations
|
|
141
160
|
├── skills/ # 18 SKILL.md files (domain expertise)
|
|
142
161
|
├── prompts/ # System prompt + command templates
|
|
143
162
|
├── context/ # SDK7 reference docs + asset catalog
|
package/dist/index.js
CHANGED
|
@@ -51,6 +51,7 @@ for (const ext of extensions) {
|
|
|
51
51
|
args.push("-e", join(extDir, ext));
|
|
52
52
|
}
|
|
53
53
|
args.push("-e", join(extDir, "plan-mode/index.ts"));
|
|
54
|
+
args.push("-e", join(extDir, "permissions/index.ts"));
|
|
54
55
|
// Load all skill directories
|
|
55
56
|
args.push("--skill", join(packageDir, "skills"));
|
|
56
57
|
// Load prompt templates (review, explain — NOT system.md since that's the system prompt)
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAEzC,uFAAuF;AACvF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AACtD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,QAAQ,CAAC;AAC7C,CAAC;AAED,wEAAwE;AACxE,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AACrD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;IAC9B,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3F,CAAC;AAED,yCAAyC;AACzC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,iFAAiF;AACjF,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACtC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC;IACzE,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;AAC7C,CAAC;AAED,sBAAsB;AACtB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAC9C,MAAM,UAAU,GAAG;IACjB,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,eAAe;IACf,cAAc;IACd,qBAAqB;IACrB,iBAAiB;IACjB,eAAe;IACf,qBAAqB;IACrB,eAAe;IACf,cAAc;CACf,CAAC;AACF,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;IAC7B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;AACrC,CAAC;AACD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Gate Extension
|
|
3
|
+
*
|
|
4
|
+
* Prompts for confirmation before dangerous bash commands or writes to
|
|
5
|
+
* sensitive files. Blocks entirely in non-interactive mode.
|
|
6
|
+
* Disable with --no-permissions flag (for CI/container environments).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
10
|
+
import { classifyBashCommand, classifyFilePath } from "./utils.js";
|
|
11
|
+
|
|
12
|
+
function blockResult(reason: string, detail: string): { block: true; reason: string } {
|
|
13
|
+
return {
|
|
14
|
+
block: true,
|
|
15
|
+
reason: `Blocked: ${reason}\n${detail}\nUse --no-permissions to allow in non-interactive mode.`,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function denyResult(reason: string): { block: true; reason: string } {
|
|
20
|
+
return { block: true, reason: `User denied: ${reason}` };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const ALLOW = "Allow";
|
|
24
|
+
const ALWAYS = "Always allow";
|
|
25
|
+
const DENY = "Deny";
|
|
26
|
+
|
|
27
|
+
const extension: ExtensionFactory = (pi) => {
|
|
28
|
+
const sessionAllow = new Set<string>();
|
|
29
|
+
|
|
30
|
+
pi.registerFlag("no-permissions", {
|
|
31
|
+
description: "Disable permission gate (skip confirmation prompts for dangerous operations)",
|
|
32
|
+
type: "boolean",
|
|
33
|
+
default: false,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
37
|
+
if (pi.getFlag("no-permissions") === true) return;
|
|
38
|
+
|
|
39
|
+
const toolName = event.toolName as string;
|
|
40
|
+
|
|
41
|
+
if (toolName === "bash") {
|
|
42
|
+
const command = (event.input as { command?: string }).command ?? "";
|
|
43
|
+
const reason = classifyBashCommand(command);
|
|
44
|
+
if (!reason || sessionAllow.has(reason)) return;
|
|
45
|
+
|
|
46
|
+
if (!ctx.hasUI) return blockResult(reason, `Command: ${command}`);
|
|
47
|
+
|
|
48
|
+
const choice = await ctx.ui.select(
|
|
49
|
+
`${reason}\nCommand: ${command}`,
|
|
50
|
+
[ALLOW, ALWAYS, DENY],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (choice === ALWAYS) { sessionAllow.add(reason); return; }
|
|
54
|
+
if (choice === ALLOW) return;
|
|
55
|
+
return denyResult(reason);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (toolName === "write" || toolName === "edit") {
|
|
59
|
+
const filePath = (event.input as { path?: string }).path ?? "";
|
|
60
|
+
const reason = filePath ? classifyFilePath(filePath, ctx.cwd) : null;
|
|
61
|
+
if (!reason || sessionAllow.has(reason)) return;
|
|
62
|
+
|
|
63
|
+
if (!ctx.hasUI) return blockResult(reason, `Path: ${filePath}`);
|
|
64
|
+
|
|
65
|
+
const choice = await ctx.ui.select(
|
|
66
|
+
`${reason}\nFile: ${filePath}`,
|
|
67
|
+
[ALLOW, ALWAYS, DENY],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (choice === ALWAYS) { sessionAllow.add(reason); return; }
|
|
71
|
+
if (choice === ALLOW) return;
|
|
72
|
+
return denyResult(reason);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default extension;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure classification functions for permission gating.
|
|
3
|
+
* No pi dependency — extracted for testability.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { resolve, relative } from "node:path";
|
|
7
|
+
|
|
8
|
+
interface DenylistEntry {
|
|
9
|
+
pattern: RegExp;
|
|
10
|
+
reason: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DANGEROUS_BASH_PATTERNS: DenylistEntry[] = [
|
|
14
|
+
{ pattern: /\brm\b/, reason: "Deletes files or directories" },
|
|
15
|
+
{ pattern: /\brmdir\b/, reason: "Removes directories" },
|
|
16
|
+
{ pattern: /\bmv\b/, reason: "Moves or renames files" },
|
|
17
|
+
{ pattern: /\bchmod\b/, reason: "Changes file permissions" },
|
|
18
|
+
{ pattern: /\bchown\b/, reason: "Changes file ownership" },
|
|
19
|
+
{ pattern: /\bdd\b/, reason: "Low-level disk write" },
|
|
20
|
+
{ pattern: /\bshred\b/, reason: "Securely deletes files" },
|
|
21
|
+
{ pattern: /\bsudo\b/, reason: "Runs with elevated privileges" },
|
|
22
|
+
{ pattern: /\bsu\b/, reason: "Switches user" },
|
|
23
|
+
{ pattern: /\bkill\b/, reason: "Terminates a process" },
|
|
24
|
+
{ pattern: /\bpkill\b/, reason: "Terminates processes by name" },
|
|
25
|
+
{ pattern: /\bkillall\b/, reason: "Terminates all matching processes" },
|
|
26
|
+
{ pattern: /\bgit\s+push\b/i, reason: "Pushes to a remote repository" },
|
|
27
|
+
{ pattern: /\bgit\s+reset\b/i, reason: "Resets git state" },
|
|
28
|
+
{ pattern: /\bgit\s+rebase\b/i, reason: "Rebases git history" },
|
|
29
|
+
{ pattern: /\bnpm\s+install\b/i, reason: "Installs npm packages (may run postinstall scripts)" },
|
|
30
|
+
{ pattern: /\bnpm\s+uninstall\b/i, reason: "Uninstalls npm packages" },
|
|
31
|
+
{ pattern: /\bnpm\s+publish\b/i, reason: "Publishes to npm registry" },
|
|
32
|
+
{ pattern: /\bcurl\b.*(-X\s*(POST|PUT|DELETE|PATCH)|--data|-d\s|-F\s|--upload)/i, reason: "Sends data via HTTP" },
|
|
33
|
+
{ pattern: /\bssh\b/, reason: "Opens remote shell connection" },
|
|
34
|
+
{ pattern: /\bscp\b/, reason: "Copies files to/from remote host" },
|
|
35
|
+
{ pattern: /\breboot\b/, reason: "Reboots the system" },
|
|
36
|
+
{ pattern: /\bshutdown\b/, reason: "Shuts down the system" },
|
|
37
|
+
{ pattern: /\btee\b/, reason: "Writes to files via pipe" },
|
|
38
|
+
{ pattern: /(^|\s)>(?![>&])\s*\S/, reason: "Redirects output to a file" },
|
|
39
|
+
{ pattern: />>/, reason: "Appends output to a file" },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const SENSITIVE_FILE_PATTERNS: DenylistEntry[] = [
|
|
43
|
+
{ pattern: /\.env($|\.)/, reason: "Environment variables (may contain secrets)" },
|
|
44
|
+
{ pattern: /\.pem$/, reason: "Private key file" },
|
|
45
|
+
{ pattern: /\.key$/, reason: "Private key file" },
|
|
46
|
+
{ pattern: /\.crt$/, reason: "Certificate file" },
|
|
47
|
+
{ pattern: /credentials/i, reason: "Credentials file" },
|
|
48
|
+
{ pattern: /\.secret/i, reason: "Secret file" },
|
|
49
|
+
{ pattern: /(^|\/)package\.json$/, reason: "Package manifest (affects dependencies)" },
|
|
50
|
+
{ pattern: /(^|\/)tsconfig\.json$/, reason: "TypeScript configuration" },
|
|
51
|
+
{ pattern: /(^|\/)\.git\//, reason: "Git internal file" },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
function findMatchingReason(entries: DenylistEntry[], value: string): string | null {
|
|
55
|
+
for (const { pattern, reason } of entries) {
|
|
56
|
+
if (pattern.test(value)) return reason;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns a reason string if the command is dangerous, or null if safe.
|
|
63
|
+
* Callers can use the return value as both a boolean guard and a message.
|
|
64
|
+
*/
|
|
65
|
+
export function classifyBashCommand(command: string): string | null {
|
|
66
|
+
return findMatchingReason(DANGEROUS_BASH_PATTERNS, command);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns a reason string if the file path is sensitive or outside the
|
|
71
|
+
* project root, or null if safe.
|
|
72
|
+
*/
|
|
73
|
+
export function classifyFilePath(filePath: string, projectRoot: string): string | null {
|
|
74
|
+
const resolved = resolve(projectRoot, filePath);
|
|
75
|
+
const rel = relative(projectRoot, resolved);
|
|
76
|
+
|
|
77
|
+
if (rel.startsWith("..")) {
|
|
78
|
+
return "File is outside the project root";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return findMatchingReason(SENSITIVE_FILE_PATTERNS, filePath);
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dcl-regenesislabs/opendcl",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-22239132687.commit-eccf1dd",
|
|
4
4
|
"description": "AI coding assistant for Decentraland SDK7 scene development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"prompts/",
|
|
62
62
|
"context/"
|
|
63
63
|
],
|
|
64
|
-
"commit": "
|
|
64
|
+
"commit": "eccf1ddf7568056584700b927142426e95be390d"
|
|
65
65
|
}
|