@codio-ai/opencode-authoring-agent 0.1.2 → 0.1.4
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/bin/codio-authoring-register.js +0 -22
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/index.js +46170 -40
- package/dist/tools/fetch-course.d.ts +17 -0
- package/dist/tools/fetch-course.d.ts.map +1 -0
- package/dist/tools/fetch-course.test.d.ts +2 -0
- package/dist/tools/fetch-course.test.d.ts.map +1 -0
- package/dist/tools/index.d.ts +36 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/publish-course.d.ts +20 -0
- package/dist/tools/publish-course.d.ts.map +1 -0
- package/dist/tools/publish-course.test.d.ts +2 -0
- package/dist/tools/publish-course.test.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/skills/reference-codio-api/SKILL.md +149 -0
- package/src/skills/codio-fetch/SKILL.md +0 -153
- package/src/skills/codio-fetch/scripts/extract-course-export.sh +0 -69
- package/src/skills/codio-fetch/scripts/run-course-export.sh +0 -196
- package/src/skills/codio-publish/SKILL.md +0 -140
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const fetchCourseInputSchema: z.ZodObject<{
|
|
3
|
+
workspace: z.ZodString;
|
|
4
|
+
courseId: z.ZodString;
|
|
5
|
+
outputDir: z.ZodOptional<z.ZodString>;
|
|
6
|
+
domain: z.ZodOptional<z.ZodString>;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
export type FetchCourseInput = z.infer<typeof fetchCourseInputSchema>;
|
|
9
|
+
export interface FetchCourseResult {
|
|
10
|
+
outputDir: string;
|
|
11
|
+
manifestPath: string;
|
|
12
|
+
assignmentsCsvPath: string;
|
|
13
|
+
moduleCount: number;
|
|
14
|
+
assignmentCount: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function fetchCourseHandler(raw: unknown): Promise<FetchCourseResult>;
|
|
17
|
+
//# sourceMappingURL=fetch-course.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-course.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-course.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,sBAAsB;;;;;iBAQjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAoED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0GjF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-course.test.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-course.test.ts"],"names":[],"mappings":""}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -58,5 +58,41 @@ export declare const validate_guide: {
|
|
|
58
58
|
workspace: string;
|
|
59
59
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
|
|
60
60
|
};
|
|
61
|
+
export declare const fetch_course: {
|
|
62
|
+
description: string;
|
|
63
|
+
args: {
|
|
64
|
+
workspace: import("zod").ZodString;
|
|
65
|
+
courseId: import("zod").ZodString;
|
|
66
|
+
outputDir: import("zod").ZodOptional<import("zod").ZodString>;
|
|
67
|
+
domain: import("zod").ZodOptional<import("zod").ZodString>;
|
|
68
|
+
};
|
|
69
|
+
execute(args: {
|
|
70
|
+
workspace: string;
|
|
71
|
+
courseId: string;
|
|
72
|
+
outputDir?: string | undefined;
|
|
73
|
+
domain?: string | undefined;
|
|
74
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
|
|
75
|
+
};
|
|
76
|
+
export declare const publish_course: {
|
|
77
|
+
description: string;
|
|
78
|
+
args: {
|
|
79
|
+
manifestPath: import("zod").ZodString;
|
|
80
|
+
dryRun: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
81
|
+
force: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
82
|
+
module: import("zod").ZodOptional<import("zod").ZodString>;
|
|
83
|
+
assignment: import("zod").ZodOptional<import("zod").ZodString>;
|
|
84
|
+
changelog: import("zod").ZodOptional<import("zod").ZodString>;
|
|
85
|
+
stack: import("zod").ZodOptional<import("zod").ZodString>;
|
|
86
|
+
};
|
|
87
|
+
execute(args: {
|
|
88
|
+
manifestPath: string;
|
|
89
|
+
dryRun?: boolean | undefined;
|
|
90
|
+
force?: boolean | undefined;
|
|
91
|
+
module?: string | undefined;
|
|
92
|
+
assignment?: string | undefined;
|
|
93
|
+
changelog?: string | undefined;
|
|
94
|
+
stack?: string | undefined;
|
|
95
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
|
|
96
|
+
};
|
|
61
97
|
export declare const tools: Record<string, unknown>;
|
|
62
98
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAOtB,CAAC;AAOH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;CAkB5B,CAAC;AAIH,eAAO,MAAM,cAAc;;;;;;;;CAOzB,CAAC;AAIH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;CAOvB,CAAC;AAIH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;CAOzB,CAAC;AAQH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAMzC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const publishCourseInputSchema: z.ZodObject<{
|
|
3
|
+
manifestPath: z.ZodString;
|
|
4
|
+
dryRun: z.ZodOptional<z.ZodBoolean>;
|
|
5
|
+
force: z.ZodOptional<z.ZodBoolean>;
|
|
6
|
+
module: z.ZodOptional<z.ZodString>;
|
|
7
|
+
assignment: z.ZodOptional<z.ZodString>;
|
|
8
|
+
changelog: z.ZodOptional<z.ZodString>;
|
|
9
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type PublishCourseInput = z.infer<typeof publishCourseInputSchema>;
|
|
12
|
+
export interface PublishCourseResult {
|
|
13
|
+
published: number;
|
|
14
|
+
created: number;
|
|
15
|
+
unchanged: number;
|
|
16
|
+
skipped: number;
|
|
17
|
+
dryRun: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function publishCourseHandler(raw: unknown): Promise<PublishCourseResult>;
|
|
20
|
+
//# sourceMappingURL=publish-course.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-course.d.ts","sourceRoot":"","sources":["../../src/tools/publish-course.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,wBAAwB;;;;;;;;iBAQnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AA6DD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuHrF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-course.test.d.ts","sourceRoot":"","sources":["../../src/tools/publish-course.test.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codio-ai/opencode-authoring-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Opencode plugin for authoring Codio guide assignments — orchestrator, subagents, tools, and skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"codio-authoring-unregister": "./bin/codio-authoring-unregister.js"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "bun build src/index.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod &&
|
|
20
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod && tsc --emitDeclarationOnly",
|
|
21
21
|
"postinstall": "node ./bin/codio-authoring-register.js || true",
|
|
22
22
|
"preuninstall": "node ./bin/codio-authoring-unregister.js || true",
|
|
23
23
|
"test": "bun test",
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reference-codio-api
|
|
3
|
+
description: Reference for the Codio platform API tools — fetch_course and publish_course. Course manifest format, output structure, credential requirements, and troubleshooting. Use when working with fetched courses or publishing assignments.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Codio API Tools Reference
|
|
7
|
+
|
|
8
|
+
## Credentials
|
|
9
|
+
|
|
10
|
+
Both tools read credentials from environment variables. Set these on the Codio box before using either tool:
|
|
11
|
+
|
|
12
|
+
| Variable | Required | Description |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `CODIO_CLIENT_ID` | Yes | Codio API client ID |
|
|
15
|
+
| `CODIO_CLIENT_SECRET` | Yes | Codio API client secret |
|
|
16
|
+
| `CODIO_DOMAIN` | No | Codio domain, default `codio.com` |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## `fetch_course` — Pull a course from Codio
|
|
21
|
+
|
|
22
|
+
Authenticates with the Codio API, downloads the course export, and writes an AI-ready project to the workspace.
|
|
23
|
+
|
|
24
|
+
### Inputs
|
|
25
|
+
|
|
26
|
+
| Field | Required | Description |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `workspace` | Yes | Absolute path to the Codio workspace root |
|
|
29
|
+
| `courseId` | Yes | Codio course ID (32-char hex string) |
|
|
30
|
+
| `outputDir` | No | Output folder name, default `course-project` |
|
|
31
|
+
| `domain` | No | Codio domain, default `codio.com` |
|
|
32
|
+
|
|
33
|
+
### Output
|
|
34
|
+
|
|
35
|
+
Returns `{ outputDir, manifestPath, assignmentsCsvPath, moduleCount, assignmentCount }`.
|
|
36
|
+
|
|
37
|
+
Writes to `<outputDir>/` inside the workspace:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
<outputDir>/
|
|
41
|
+
├── course-manifest.json ← machine-readable index; source of truth for publish_course
|
|
42
|
+
├── course-assignments.csv ← flat CSV for review
|
|
43
|
+
└── course-content/
|
|
44
|
+
└── <module-folder>/
|
|
45
|
+
└── <assignment-folder>/
|
|
46
|
+
└── .guides/ ← guide pages, assessments, images
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## `publish_course` — Push assignments back to Codio
|
|
52
|
+
|
|
53
|
+
Reads `course-manifest.json` from the project folder and publishes each assignment to Codio.
|
|
54
|
+
|
|
55
|
+
### Inputs
|
|
56
|
+
|
|
57
|
+
| Field | Required | Description |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `manifestPath` | Yes | Absolute path to `course-manifest.json` |
|
|
60
|
+
| `dryRun` | No | Preview actions without making changes |
|
|
61
|
+
| `force` | No | Publish all, bypassing change detection |
|
|
62
|
+
| `module` | No | Only process this module (name or folder) |
|
|
63
|
+
| `assignment` | No | Only process this assignment (name or folder) |
|
|
64
|
+
| `changelog` | No | Changelog text applied to all publishes |
|
|
65
|
+
| `stack` | No | Force a specific stack ID for all assignments |
|
|
66
|
+
|
|
67
|
+
### Output
|
|
68
|
+
|
|
69
|
+
Returns `{ published, created, unchanged, skipped }`.
|
|
70
|
+
|
|
71
|
+
Writes back any new IDs to `course-manifest.json` immediately after each resource is created.
|
|
72
|
+
|
|
73
|
+
### Change detection
|
|
74
|
+
|
|
75
|
+
The tool skips existing assignments whose content hash matches the stored `contentHash` in the manifest. Pass `force: true` to bypass. New assignments (no `id`) are always published.
|
|
76
|
+
|
|
77
|
+
### Stacks
|
|
78
|
+
|
|
79
|
+
- **New assignments** always use the default stack.
|
|
80
|
+
- **Existing assignments** preserve their current stack unless `stack` is supplied.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## course-manifest.json format
|
|
85
|
+
|
|
86
|
+
Created by `fetch_course`, consumed by `publish_course`. Both tools write back IDs as resources are created/resolved.
|
|
87
|
+
|
|
88
|
+
```jsonc
|
|
89
|
+
{
|
|
90
|
+
"schemaVersion": 2,
|
|
91
|
+
"courseId": "c87b4540de108faad55c8cefe7847492",
|
|
92
|
+
"exportedAt": "2026-06-12T10:00:00.000Z",
|
|
93
|
+
"counts": { "modules": 2, "assignments": 6 },
|
|
94
|
+
"paths": {
|
|
95
|
+
"assignmentsCsv": "course-assignments.csv",
|
|
96
|
+
"courseContent": "course-content"
|
|
97
|
+
},
|
|
98
|
+
"modules": [
|
|
99
|
+
{
|
|
100
|
+
"id": "...", // Resolved or created by publish_course; written back
|
|
101
|
+
"name": "Module Name",
|
|
102
|
+
"folder": "module-name",
|
|
103
|
+
"path": "course-content/module-name",
|
|
104
|
+
"assignmentCount": 3,
|
|
105
|
+
"assignments": [
|
|
106
|
+
{
|
|
107
|
+
"id": "...", // Created by publish_course if empty; written back
|
|
108
|
+
"name": "Assignment Name",
|
|
109
|
+
"folder": "assignment-name",
|
|
110
|
+
"path": "course-content/module-name/assignment-name",
|
|
111
|
+
"contentHash": "..." // Written after each successful publish; used for change detection
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Folder slug rules
|
|
120
|
+
|
|
121
|
+
Codio uses lowercase slugs: normalise to ASCII, lowercase, replace each non-alphanumeric character with `-`, trim leading/trailing hyphens. Example: `"1.1 – Intro"` → `"1-1---intro"`.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Typical workflow
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
fetch_course(courseId)
|
|
129
|
+
→ course-project/course-manifest.json + course-content/...
|
|
130
|
+
|
|
131
|
+
[edit assignment pages using authoring tools]
|
|
132
|
+
|
|
133
|
+
publish_course(manifestPath, dryRun: true) ← preview first
|
|
134
|
+
publish_course(manifestPath) ← publish for real
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Troubleshooting
|
|
140
|
+
|
|
141
|
+
| Error | Fix |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `CODIO_CLIENT_ID ... must be set` | Set env vars on the box |
|
|
144
|
+
| `course-manifest.json not found` | Pass the absolute path to the manifest; run from or inside the project folder |
|
|
145
|
+
| `path not found` for an assignment | The folder referenced in the manifest doesn't exist — generate content there first |
|
|
146
|
+
| `Required command not found: unzip` | Install `unzip` via the box package manager |
|
|
147
|
+
| API 401 / auth error | Credentials are invalid or expired |
|
|
148
|
+
| API 429 rate limit | Library retries automatically; wait and re-run if still failing |
|
|
149
|
+
| `no module ID available` | Module creation failed earlier in the run — check the error above it |
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: codio-fetch
|
|
3
|
-
description: Fetch and export an existing Codio course into an AI-ready local project. Use when the user asks to fetch, export, download, pull, or import a course from Codio, or to create a local AI-ready project from an existing Codio course.
|
|
4
|
-
argument-hint: [--course-id <id>] [--output-dir <path>] [--domain <domain>]
|
|
5
|
-
allowed-tools: [Bash, Read, Write, Edit]
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Codio Fetch
|
|
9
|
-
|
|
10
|
-
Export an existing Codio course into a clean, AI-ready local project that can be edited and re-published with `codio-publish`.
|
|
11
|
-
|
|
12
|
-
## What this skill does
|
|
13
|
-
|
|
14
|
-
1. Loads credentials from `.env` at the workspace root (the Codio assignment directory).
|
|
15
|
-
2. Authenticates with the Codio API using `CODIO_CLIENT_ID` and `CODIO_CLIENT_SECRET`.
|
|
16
|
-
3. Fetches course metadata and builds a `course-manifest.json` with all module and assignment IDs.
|
|
17
|
-
4. Downloads the course source export ZIP from Codio.
|
|
18
|
-
5. Extracts the ZIP into `course-content/`, expanding nested `.tar.zst` assignment archives.
|
|
19
|
-
6. Writes `course-assignments.csv` as a flat index for easy review.
|
|
20
|
-
7. Cleans up temporary download files.
|
|
21
|
-
|
|
22
|
-
The output is a self-contained project folder ready for `codio-publish` to read back.
|
|
23
|
-
|
|
24
|
-
## Output structure
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
<output-dir>/ ← default: ./course-project
|
|
28
|
-
├── course-manifest.json ← machine-readable index with all IDs and paths
|
|
29
|
-
├── course-assignments.csv ← flat CSV listing all modules and assignments
|
|
30
|
-
└── course-content/
|
|
31
|
-
└── <module-folder>/
|
|
32
|
-
└── <assignment-folder>/
|
|
33
|
-
└── .guides/ ← guide pages, assessments, images
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
The `<output-dir>` is always a named subfolder, never at the workspace root itself.
|
|
37
|
-
|
|
38
|
-
## Prerequisites
|
|
39
|
-
|
|
40
|
-
Before running, verify:
|
|
41
|
-
|
|
42
|
-
1. **`.env` file** — must exist at the workspace root:
|
|
43
|
-
```
|
|
44
|
-
CODIO_CLIENT_ID=your-client-id
|
|
45
|
-
CODIO_CLIENT_SECRET=your-client-secret
|
|
46
|
-
CODIO_COURSE_ID=your-course-id # optional if passing --course-id
|
|
47
|
-
CODIO_DOMAIN=codio.com # optional, defaults to codio.com
|
|
48
|
-
```
|
|
49
|
-
The script finds `.env` by walking up from the current directory, so it works from anywhere inside the workspace.
|
|
50
|
-
|
|
51
|
-
2. **Check `.env` is in place:**
|
|
52
|
-
```bash
|
|
53
|
-
[ -f .env ] && echo "found" || echo "missing — create .env before running"
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
3. **Required system tools:** `node`, `unzip`, `tar`, `find`
|
|
57
|
-
|
|
58
|
-
## Running the export
|
|
59
|
-
|
|
60
|
-
Run from the workspace root (the Codio assignment directory):
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
bash ~/.config/opencode/skills/codio-fetch/scripts/run-course-export.sh --course-id <id>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Options
|
|
67
|
-
|
|
68
|
-
| Option | Description |
|
|
69
|
-
|---|---|
|
|
70
|
-
| `--course-id <id>` | Codio course ID (overrides `CODIO_COURSE_ID` from `.env`) |
|
|
71
|
-
| `--output-dir <path>` | Output project folder (default: `./course-project`) |
|
|
72
|
-
| `--domain <domain>` | Codio domain (default: `codio.com` or `CODIO_DOMAIN` from `.env`) |
|
|
73
|
-
|
|
74
|
-
### Examples
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
# Use course ID from .env, output to ./course-project
|
|
78
|
-
bash ~/.config/opencode/skills/codio-fetch/scripts/run-course-export.sh
|
|
79
|
-
|
|
80
|
-
# Specify course ID and custom output folder
|
|
81
|
-
bash ~/.config/opencode/skills/codio-fetch/scripts/run-course-export.sh \
|
|
82
|
-
--course-id c87b4540de108faad55c8cefe7847492 \
|
|
83
|
-
--output-dir ./my-java-course
|
|
84
|
-
|
|
85
|
-
# Use a staging Codio domain
|
|
86
|
-
bash ~/.config/opencode/skills/codio-fetch/scripts/run-course-export.sh \
|
|
87
|
-
--course-id c87b4540de108faad55c8cefe7847492 \
|
|
88
|
-
--domain codio.co.uk
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## How credentials are loaded
|
|
92
|
-
|
|
93
|
-
The shell script walks up from the current directory looking for `.env`, then sources it with `set -a; . .env; set +a`. Values in `.env` **override** anything already in the environment.
|
|
94
|
-
|
|
95
|
-
The `.env` is shared across all Codio skills (`codio-fetch`, `codio-publish`). There is one `.env` at the workspace root, not one per skill.
|
|
96
|
-
|
|
97
|
-
## Environment variables
|
|
98
|
-
|
|
99
|
-
| Variable | Required | Description |
|
|
100
|
-
|---|---|---|
|
|
101
|
-
| `CODIO_CLIENT_ID` | Yes | Codio API client ID |
|
|
102
|
-
| `CODIO_CLIENT_SECRET` | Yes | Codio API client secret |
|
|
103
|
-
| `CODIO_COURSE_ID` | No | Course ID (can be passed as `--course-id` instead) |
|
|
104
|
-
| `CODIO_DOMAIN` | No | Codio domain, default `codio.com` |
|
|
105
|
-
|
|
106
|
-
## After the export
|
|
107
|
-
|
|
108
|
-
- Open `<output-dir>/course-manifest.json` to see the full module/assignment hierarchy with IDs.
|
|
109
|
-
- Open `<output-dir>/course-assignments.csv` for a flat spreadsheet view.
|
|
110
|
-
- Assignment content is in `<output-dir>/course-content/<module>/<assignment>/`.
|
|
111
|
-
- Use `codio-publish` to push edits back to Codio — the manifest with existing IDs is already in place.
|
|
112
|
-
|
|
113
|
-
## Troubleshooting
|
|
114
|
-
|
|
115
|
-
| Error | Fix |
|
|
116
|
-
|---|---|
|
|
117
|
-
| `Missing required environment variable: CODIO_CLIENT_ID` | Create or fix `.env` at workspace root |
|
|
118
|
-
| `Missing course id` | Pass `--course-id <id>` or set `CODIO_COURSE_ID` in `.env` |
|
|
119
|
-
| `Required command not found: unzip` | Install `unzip` via your package manager |
|
|
120
|
-
| API 401 / auth error | Credentials in `.env` are invalid or expired |
|
|
121
|
-
|
|
122
|
-
## Manifest format
|
|
123
|
-
|
|
124
|
-
`course-manifest.json` produced by this skill is directly compatible with `codio-publish`. All assignment IDs are pre-populated, so a subsequent publish run will update existing assignments rather than create new ones.
|
|
125
|
-
|
|
126
|
-
```jsonc
|
|
127
|
-
{
|
|
128
|
-
"schemaVersion": 2,
|
|
129
|
-
"courseId": "...",
|
|
130
|
-
"exportedAt": "...",
|
|
131
|
-
"counts": { "modules": 2, "assignments": 6 },
|
|
132
|
-
"paths": {
|
|
133
|
-
"assignmentsCsv": "course-assignments.csv",
|
|
134
|
-
"courseContent": "course-content"
|
|
135
|
-
},
|
|
136
|
-
"modules": [
|
|
137
|
-
{
|
|
138
|
-
"name": "Module Name",
|
|
139
|
-
"folder": "module-name",
|
|
140
|
-
"path": "course-content/module-name",
|
|
141
|
-
"assignmentCount": 3,
|
|
142
|
-
"assignments": [
|
|
143
|
-
{
|
|
144
|
-
"id": "...",
|
|
145
|
-
"name": "Assignment Name",
|
|
146
|
-
"folder": "assignment-name",
|
|
147
|
-
"path": "course-content/module-name/assignment-name"
|
|
148
|
-
}
|
|
149
|
-
]
|
|
150
|
-
}
|
|
151
|
-
]
|
|
152
|
-
}
|
|
153
|
-
```
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -Eeuo pipefail
|
|
3
|
-
shopt -s nullglob
|
|
4
|
-
|
|
5
|
-
log() {
|
|
6
|
-
printf '[%s] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*"
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
fail() {
|
|
10
|
-
log "ERROR: $*"
|
|
11
|
-
exit 1
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
require_command() {
|
|
15
|
-
command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
extract_assignment_archives() {
|
|
19
|
-
local root_dir="$1"
|
|
20
|
-
local archive_count=0
|
|
21
|
-
|
|
22
|
-
while IFS= read -r -d '' archive_path; do
|
|
23
|
-
archive_count=$((archive_count + 1))
|
|
24
|
-
|
|
25
|
-
local archive_dir archive_name destination_dir
|
|
26
|
-
archive_dir="$(dirname "$archive_path")"
|
|
27
|
-
archive_name="$(basename "$archive_path")"
|
|
28
|
-
destination_dir="$archive_dir/${archive_name%.tar.zst}"
|
|
29
|
-
|
|
30
|
-
rm -rf "$destination_dir"
|
|
31
|
-
mkdir -p "$destination_dir"
|
|
32
|
-
|
|
33
|
-
log "Extracting nested archive ${archive_name}"
|
|
34
|
-
tar --zstd -xf "$archive_path" -C "$destination_dir"
|
|
35
|
-
rm -f "$archive_path"
|
|
36
|
-
done < <(find "$root_dir" -type f -name '*.tar.zst' -print0)
|
|
37
|
-
|
|
38
|
-
log "Expanded ${archive_count} nested assignment archive(s)"
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
main() {
|
|
42
|
-
require_command unzip
|
|
43
|
-
require_command tar
|
|
44
|
-
require_command find
|
|
45
|
-
require_command cp
|
|
46
|
-
|
|
47
|
-
local export_zip_path="${1:-}"
|
|
48
|
-
local staging_dir="${2:-}"
|
|
49
|
-
local visible_output_dir="${3:-}"
|
|
50
|
-
|
|
51
|
-
[[ -n "$export_zip_path" ]] || fail 'Usage: extract-course-export.sh <export-zip-path> <staging-dir> <visible-output-dir>'
|
|
52
|
-
[[ -n "$staging_dir" ]] || fail 'Missing staging directory'
|
|
53
|
-
[[ -n "$visible_output_dir" ]] || fail 'Missing visible output directory'
|
|
54
|
-
[[ -f "$export_zip_path" ]] || fail "Export ZIP not found: $export_zip_path"
|
|
55
|
-
|
|
56
|
-
rm -rf "$staging_dir" "$visible_output_dir"
|
|
57
|
-
mkdir -p "$staging_dir" "$visible_output_dir"
|
|
58
|
-
|
|
59
|
-
log "Unzipping $(basename "$export_zip_path") into hidden staging"
|
|
60
|
-
unzip -q "$export_zip_path" -d "$staging_dir"
|
|
61
|
-
|
|
62
|
-
extract_assignment_archives "$staging_dir"
|
|
63
|
-
|
|
64
|
-
cp -R "$staging_dir"/. "$visible_output_dir"
|
|
65
|
-
|
|
66
|
-
log "Published extracted course content to $visible_output_dir"
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
main "$@"
|